Merge "Show selection handles even when toolbar is empty."
diff --git a/Android.bp b/Android.bp
index 5c1ccb7..2c4963c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@
             // runtime, as well as the only protos that are actually
             // needed by the device.
             srcs: [
+                "core/proto/android/os/cpuinfo.proto",
                 "core/proto/android/os/kernelwake.proto",
                 "core/proto/android/os/pagetypeinfo.proto",
                 "core/proto/android/os/procrank.proto",
@@ -81,6 +82,7 @@
     ],
 
     srcs: [
+        "core/proto/android/os/cpuinfo.proto",
         "core/proto/android/os/kernelwake.proto",
         "core/proto/android/os/pagetypeinfo.proto",
         "core/proto/android/os/procrank.proto",
diff --git a/Android.mk b/Android.mk
index a7cb362..b847425 100644
--- a/Android.mk
+++ b/Android.mk
@@ -137,6 +137,7 @@
 	../../system/bt/binder/android/bluetooth/IBluetoothPbap.aidl \
 	../../system/bt/binder/android/bluetooth/IBluetoothPbapClient.aidl \
 	../../system/bt/binder/android/bluetooth/IBluetoothSap.aidl \
+	../../system/bt/binder/android/bluetooth/IBluetoothSocketManager.aidl \
 	../../system/bt/binder/android/bluetooth/IBluetoothStateChangeCallback.aidl \
 	../../system/bt/binder/android/bluetooth/IBluetoothHeadsetClient.aidl \
 	../../system/bt/binder/android/bluetooth/IBluetoothHidDevice.aidl \
@@ -158,6 +159,7 @@
 	core/java/android/content/ISyncServiceAdapter.aidl \
 	core/java/android/content/ISyncStatusObserver.aidl \
 	core/java/android/content/om/IOverlayManager.aidl \
+	core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl \
 	core/java/android/content/pm/IDexModuleRegisterCallback.aidl \
 	core/java/android/content/pm/ILauncherApps.aidl \
 	core/java/android/content/pm/IOnAppsChangedListener.aidl \
@@ -398,6 +400,7 @@
 	core/java/com/android/internal/backup/IObbBackupService.aidl \
 	core/java/com/android/internal/car/ICarServiceHelper.aidl \
 	core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl \
+	core/java/com/android/internal/net/INetworkWatchlistManager.aidl \
 	core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl \
 	core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl \
 	core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \
@@ -1555,6 +1558,7 @@
 LOCAL_SOURCE_FILES_ALL_GENERATED := true
 LOCAL_SRC_FILES := \
     tools/streaming_proto/stream.proto \
+    cmds/am/proto/instrumentation_data.proto \
     $(call all-proto-files-under, core/proto) \
     $(call all-proto-files-under, libs/incident/proto)
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 50a5974..711c12d 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,3 +7,4 @@
                       services/print/
                       services/usb/
 
+api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT}
diff --git a/api/current.txt b/api/current.txt
index 6a8bb01..387e815 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3609,11 +3609,11 @@
     method public android.transition.Scene getContentScene();
     method public android.transition.TransitionManager getContentTransitionManager();
     method public android.view.View getCurrentFocus();
-    method public android.app.FragmentManager getFragmentManager();
+    method public deprecated android.app.FragmentManager getFragmentManager();
     method public android.content.Intent getIntent();
     method public java.lang.Object getLastNonConfigurationInstance();
     method public android.view.LayoutInflater getLayoutInflater();
-    method public android.app.LoaderManager getLoaderManager();
+    method public deprecated android.app.LoaderManager getLoaderManager();
     method public java.lang.String getLocalClassName();
     method public int getMaxNumPictureInPictureActions();
     method public final android.media.session.MediaController getMediaController();
@@ -3653,7 +3653,7 @@
     method public void onActionModeStarted(android.view.ActionMode);
     method public void onActivityReenter(int, android.content.Intent);
     method protected void onActivityResult(int, int, android.content.Intent);
-    method public void onAttachFragment(android.app.Fragment);
+    method public deprecated void onAttachFragment(android.app.Fragment);
     method public void onAttachedToWindow();
     method public void onBackPressed();
     method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence);
@@ -3795,8 +3795,8 @@
     method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
     method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
     method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
-    method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
-    method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+    method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
     method public boolean startActivityIfNeeded(android.content.Intent, int);
     method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
     method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4455,7 +4455,7 @@
     method public void unregisterForContextMenu(android.view.View);
   }
 
-  public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+  public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
     ctor public DialogFragment();
     method public void dismiss();
     method public void dismissAllowingStateLoss();
@@ -4573,7 +4573,7 @@
     method public void setSelectedGroup(int);
   }
 
-  public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+  public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
     ctor public Fragment();
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final boolean equals(java.lang.Object);
@@ -4589,7 +4589,7 @@
     method public final java.lang.Object getHost();
     method public final int getId();
     method public final android.view.LayoutInflater getLayoutInflater();
-    method public android.app.LoaderManager getLoaderManager();
+    method public deprecated android.app.LoaderManager getLoaderManager();
     method public final android.app.Fragment getParentFragment();
     method public android.transition.Transition getReenterTransition();
     method public final android.content.res.Resources getResources();
@@ -4684,11 +4684,11 @@
     method public void unregisterForContextMenu(android.view.View);
   }
 
-  public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+  public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
     ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
   }
 
-  public static class Fragment.SavedState implements android.os.Parcelable {
+  public static deprecated class Fragment.SavedState implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4707,17 +4707,17 @@
     method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
   }
 
-  public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+  public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
     method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
   }
 
-  public abstract class FragmentContainer {
+  public abstract deprecated class FragmentContainer {
     ctor public FragmentContainer();
     method public abstract <T extends android.view.View> T onFindViewById(int);
     method public abstract boolean onHasView();
   }
 
-  public class FragmentController {
+  public deprecated class FragmentController {
     method public void attachHost(android.app.Fragment);
     method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
     method public void dispatchActivityCreated();
@@ -4760,7 +4760,7 @@
     method public android.os.Parcelable saveAllState();
   }
 
-  public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+  public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
     ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
     method public void onAttachFragment(android.app.Fragment);
     method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4778,7 +4778,7 @@
     method public boolean onUseFragmentManagerInflaterFactory();
   }
 
-  public abstract class FragmentManager {
+  public abstract deprecated class FragmentManager {
     ctor public FragmentManager();
     method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
     method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4809,7 +4809,7 @@
     field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
   }
 
-  public static abstract interface FragmentManager.BackStackEntry {
+  public static abstract deprecated interface FragmentManager.BackStackEntry {
     method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
     method public abstract int getBreadCrumbShortTitleRes();
     method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4818,7 +4818,7 @@
     method public abstract java.lang.String getName();
   }
 
-  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+  public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
     ctor public FragmentManager.FragmentLifecycleCallbacks();
     method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
     method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -4836,14 +4836,14 @@
     method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
   }
 
-  public static abstract interface FragmentManager.OnBackStackChangedListener {
+  public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
     method public abstract void onBackStackChanged();
   }
 
-  public class FragmentManagerNonConfig {
+  public deprecated class FragmentManagerNonConfig {
   }
 
-  public abstract class FragmentTransaction {
+  public abstract deprecated class FragmentTransaction {
     ctor public FragmentTransaction();
     method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
     method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -4943,6 +4943,7 @@
     method public void setInTouchMode(boolean);
     method public void start();
     method public android.app.Activity startActivitySync(android.content.Intent);
+    method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
     method public deprecated void startAllocCounting();
     method public void startPerformanceSnapshot();
     method public void startProfiling();
@@ -5048,7 +5049,7 @@
     method public void setSelection(int);
   }
 
-  public class ListFragment extends android.app.Fragment {
+  public deprecated class ListFragment extends android.app.Fragment {
     ctor public ListFragment();
     method public android.widget.ListAdapter getListAdapter();
     method public android.widget.ListView getListView();
@@ -5062,7 +5063,7 @@
     method public void setSelection(int);
   }
 
-  public abstract class LoaderManager {
+  public abstract deprecated class LoaderManager {
     ctor public LoaderManager();
     method public abstract void destroyLoader(int);
     method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5072,7 +5073,7 @@
     method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
   }
 
-  public static abstract interface LoaderManager.LoaderCallbacks<D> {
+  public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
     method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
     method public abstract void onLoadFinished(android.content.Loader<D>, D);
     method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -6541,6 +6542,7 @@
     field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
     field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
     field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+    field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
     field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
     field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
     field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -6846,7 +6848,8 @@
     method public static final long getMinFlexMillis();
     method public long getMinLatencyMillis();
     method public static final long getMinPeriodMillis();
-    method public int getNetworkType();
+    method public deprecated int getNetworkType();
+    method public android.net.NetworkRequest getRequiredNetwork();
     method public android.content.ComponentName getService();
     method public android.os.Bundle getTransientExtras();
     method public long getTriggerContentMaxDelay();
@@ -6866,7 +6869,8 @@
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
     field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
-    field public static final int NETWORK_TYPE_METERED = 4; // 0x4
+    field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4
+    field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6885,6 +6889,7 @@
     method public android.app.job.JobInfo.Builder setPeriodic(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
+    method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -8528,7 +8533,7 @@
     ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
   }
 
-  public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+  public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
     ctor public AsyncTaskLoader(android.content.Context);
     method public void cancelLoadInBackground();
     method public boolean isLoadInBackgroundCanceled();
@@ -9101,6 +9106,7 @@
     field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
     field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
     field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+    field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
     field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
     field public static final java.lang.String DISPLAY_SERVICE = "display";
     field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9267,7 +9273,7 @@
     method public void unregisterReceiver(android.content.BroadcastReceiver);
   }
 
-  public class CursorLoader extends android.content.AsyncTaskLoader {
+  public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
     ctor public CursorLoader(android.content.Context);
     ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public void deliverResult(android.database.Cursor);
@@ -9873,7 +9879,7 @@
     ctor public IntentSender.SendIntentException(java.lang.Exception);
   }
 
-  public class Loader<D> {
+  public deprecated class Loader<D> {
     ctor public Loader(android.content.Context);
     method public void abandon();
     method public boolean cancelLoad();
@@ -9906,15 +9912,15 @@
     method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
   }
 
-  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+  public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
     ctor public Loader.ForceLoadContentObserver();
   }
 
-  public static abstract interface Loader.OnLoadCanceledListener<D> {
+  public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
     method public abstract void onLoadCanceled(android.content.Loader<D>);
   }
 
-  public static abstract interface Loader.OnLoadCompleteListener<D> {
+  public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
     method public abstract void onLoadComplete(android.content.Loader<D>, D);
   }
 
@@ -11222,6 +11228,15 @@
 
 }
 
+package android.content.pm.crossprofile {
+
+  public class CrossProfileApps {
+    method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+    method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+  }
+
+}
+
 package android.content.res {
 
   public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -15263,6 +15278,7 @@
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+    method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
   }
 
   public static abstract class CameraCaptureSession.CaptureCallback {
@@ -15904,9 +15920,11 @@
     method public void addSurface(android.view.Surface);
     method public int describeContents();
     method public void enableSurfaceSharing();
+    method public static int getMaxSharedSurfaceCount();
     method public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
     method public java.util.List<android.view.Surface> getSurfaces();
+    method public void removeSurface(android.view.Surface);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -18272,11 +18290,13 @@
     method public synchronized void applyLocalizedPattern(java.lang.String);
     method public synchronized void applyPattern(java.lang.String);
     method public synchronized boolean areSignificantDigitsUsed();
+    method public synchronized boolean equals(java.lang.Object);
     method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
+    method public synchronized android.icu.util.Currency getCurrency();
     method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
     method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
     method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18284,8 +18304,12 @@
     method public synchronized int getGroupingSize();
     method public synchronized java.math.MathContext getMathContext();
     method public synchronized android.icu.math.MathContext getMathContextICU();
+    method public synchronized int getMaximumFractionDigits();
+    method public synchronized int getMaximumIntegerDigits();
     method public synchronized int getMaximumSignificantDigits();
     method public synchronized byte getMinimumExponentDigits();
+    method public synchronized int getMinimumFractionDigits();
+    method public synchronized int getMinimumIntegerDigits();
     method public synchronized int getMinimumSignificantDigits();
     method public synchronized int getMultiplier();
     method public synchronized java.lang.String getNegativePrefix();
@@ -18296,13 +18320,19 @@
     method public synchronized java.lang.String getPositivePrefix();
     method public synchronized java.lang.String getPositiveSuffix();
     method public synchronized java.math.BigDecimal getRoundingIncrement();
+    method public synchronized int getRoundingMode();
     method public synchronized int getSecondaryGroupingSize();
+    method public synchronized int hashCode();
     method public synchronized boolean isDecimalPatternMatchRequired();
     method public synchronized boolean isDecimalSeparatorAlwaysShown();
     method public synchronized boolean isExponentSignAlwaysShown();
+    method public synchronized boolean isGroupingUsed();
     method public synchronized boolean isParseBigDecimal();
+    method public synchronized boolean isParseIntegerOnly();
+    method public synchronized boolean isParseStrict();
     method public synchronized boolean isScientificNotation();
     method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
+    method public synchronized void setCurrency(android.icu.util.Currency);
     method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
     method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
     method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18311,10 +18341,15 @@
     method public synchronized void setExponentSignAlwaysShown(boolean);
     method public synchronized void setFormatWidth(int);
     method public synchronized void setGroupingSize(int);
+    method public synchronized void setGroupingUsed(boolean);
     method public synchronized void setMathContext(java.math.MathContext);
     method public synchronized void setMathContextICU(android.icu.math.MathContext);
+    method public synchronized void setMaximumFractionDigits(int);
+    method public synchronized void setMaximumIntegerDigits(int);
     method public synchronized void setMaximumSignificantDigits(int);
     method public synchronized void setMinimumExponentDigits(byte);
+    method public synchronized void setMinimumFractionDigits(int);
+    method public synchronized void setMinimumIntegerDigits(int);
     method public synchronized void setMinimumSignificantDigits(int);
     method public synchronized void setMultiplier(int);
     method public synchronized void setNegativePrefix(java.lang.String);
@@ -18322,12 +18357,15 @@
     method public synchronized void setPadCharacter(char);
     method public synchronized void setPadPosition(int);
     method public synchronized void setParseBigDecimal(boolean);
+    method public synchronized void setParseIntegerOnly(boolean);
     method public deprecated void setParseMaxDigits(int);
+    method public synchronized void setParseStrict(boolean);
     method public synchronized void setPositivePrefix(java.lang.String);
     method public synchronized void setPositiveSuffix(java.lang.String);
     method public synchronized void setRoundingIncrement(java.math.BigDecimal);
     method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
     method public synchronized void setRoundingIncrement(double);
+    method public synchronized void setRoundingMode(int);
     method public synchronized void setScientificNotation(boolean);
     method public synchronized void setSecondaryGroupingSize(int);
     method public synchronized void setSignificantDigitsUsed(boolean);
@@ -31816,6 +31854,14 @@
     method public static void setVmPolicy(android.os.StrictMode.VmPolicy);
   }
 
+  public static abstract interface StrictMode.OnThreadViolationListener {
+    method public abstract void onThreadViolation(android.os.strictmode.Violation);
+  }
+
+  public static abstract interface StrictMode.OnVmViolationListener {
+    method public abstract void onVmViolation(android.os.strictmode.Violation);
+  }
+
   public static final class StrictMode.ThreadPolicy {
     field public static final android.os.StrictMode.ThreadPolicy LAX;
   }
@@ -31836,6 +31882,7 @@
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+    method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
     method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
     method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -31867,6 +31914,7 @@
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+    method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
     method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
     method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
   }
@@ -31874,10 +31922,12 @@
   public final class SystemClock {
     method public static long currentThreadTimeMillis();
     method public static long elapsedRealtime();
+    method public static java.time.Clock elapsedRealtimeClock();
     method public static long elapsedRealtimeNanos();
     method public static boolean setCurrentTimeMillis(long);
     method public static void sleep(long);
     method public static long uptimeMillis();
+    method public static java.time.Clock uptimeMillisClock();
   }
 
   public class TestLooperManager {
@@ -31959,6 +32009,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
     field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
     field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+    field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
     field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
     field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
     field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -31987,6 +32038,7 @@
     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";
+    field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
     field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
     field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
     field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
@@ -32227,6 +32279,62 @@
 
 }
 
+package android.os.strictmode {
+
+  public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class CustomViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class DiskReadViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class DiskWriteViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+  }
+
+  public class InstanceCountViolation extends android.os.strictmode.Violation {
+    method public long getNumberOfInstances();
+  }
+
+  public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class NetworkViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+  }
+
+  public abstract class Violation extends java.lang.Throwable {
+  }
+
+  public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+  }
+
+}
+
 package android.preference {
 
   public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -32513,7 +32621,7 @@
     method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
   }
 
-  public abstract class PreferenceFragment extends android.app.Fragment {
+  public abstract deprecated class PreferenceFragment extends android.app.Fragment {
     ctor public PreferenceFragment();
     method public void addPreferencesFromIntent(android.content.Intent);
     method public void addPreferencesFromResource(int);
@@ -32524,7 +32632,7 @@
     method public void setPreferenceScreen(android.preference.PreferenceScreen);
   }
 
-  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+  public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
     method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
   }
 
@@ -38429,6 +38537,16 @@
     field public final int errno;
   }
 
+  public class Int32Ref {
+    ctor public Int32Ref(int);
+    field public int value;
+  }
+
+  public class Int64Ref {
+    ctor public Int64Ref(long);
+    field public long value;
+  }
+
   public final class Os {
     method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -38498,7 +38616,8 @@
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
     method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
-    method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+    method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+    method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static void setegid(int) throws android.system.ErrnoException;
@@ -38523,7 +38642,8 @@
     method public static int umask(int);
     method public static android.system.StructUtsname uname();
     method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
-    method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+    method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+    method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
     method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39219,7 +39339,9 @@
     ctor public CallAudioState(boolean, int, int);
     method public static java.lang.String audioRouteToString(int);
     method public int describeContents();
+    method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
     method public int getRoute();
+    method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
     method public int getSupportedRouteMask();
     method public boolean isMuted();
     method public void writeToParcel(android.os.Parcel, int);
@@ -39351,6 +39473,7 @@
     method public final void putExtras(android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
     method public final void removeExtras(java.lang.String...);
+    method public void requestBluetoothAudio(java.lang.String);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
@@ -39541,6 +39664,7 @@
     method public void onCanAddCallChanged(boolean);
     method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
     method public void onSilenceRinger();
+    method public final void requestBluetoothAudio(java.lang.String);
     method public final void setAudioRoute(int);
     method public final void setMuted(boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -49863,7 +49987,7 @@
     method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
   }
 
-  public class WebViewFragment extends android.app.Fragment {
+  public deprecated class WebViewFragment extends android.app.Fragment {
     ctor public WebViewFragment();
     method public android.webkit.WebView getWebView();
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index e485e964e..fb9c73d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3754,11 +3754,11 @@
     method public android.transition.Scene getContentScene();
     method public android.transition.TransitionManager getContentTransitionManager();
     method public android.view.View getCurrentFocus();
-    method public android.app.FragmentManager getFragmentManager();
+    method public deprecated android.app.FragmentManager getFragmentManager();
     method public android.content.Intent getIntent();
     method public java.lang.Object getLastNonConfigurationInstance();
     method public android.view.LayoutInflater getLayoutInflater();
-    method public android.app.LoaderManager getLoaderManager();
+    method public deprecated android.app.LoaderManager getLoaderManager();
     method public java.lang.String getLocalClassName();
     method public int getMaxNumPictureInPictureActions();
     method public final android.media.session.MediaController getMediaController();
@@ -3799,7 +3799,7 @@
     method public void onActionModeStarted(android.view.ActionMode);
     method public void onActivityReenter(int, android.content.Intent);
     method protected void onActivityResult(int, int, android.content.Intent);
-    method public void onAttachFragment(android.app.Fragment);
+    method public deprecated void onAttachFragment(android.app.Fragment);
     method public void onAttachedToWindow();
     method public void onBackPressed();
     method public deprecated void onBackgroundVisibleBehindChanged(boolean);
@@ -3943,8 +3943,8 @@
     method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
     method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
     method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
-    method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
-    method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+    method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
     method public boolean startActivityIfNeeded(android.content.Intent, int);
     method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
     method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4628,7 +4628,7 @@
     method public void unregisterForContextMenu(android.view.View);
   }
 
-  public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+  public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
     ctor public DialogFragment();
     method public void dismiss();
     method public void dismissAllowingStateLoss();
@@ -4747,7 +4747,7 @@
     method public void setSelectedGroup(int);
   }
 
-  public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+  public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
     ctor public Fragment();
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final boolean equals(java.lang.Object);
@@ -4763,7 +4763,7 @@
     method public final java.lang.Object getHost();
     method public final int getId();
     method public final android.view.LayoutInflater getLayoutInflater();
-    method public android.app.LoaderManager getLoaderManager();
+    method public deprecated android.app.LoaderManager getLoaderManager();
     method public final android.app.Fragment getParentFragment();
     method public android.transition.Transition getReenterTransition();
     method public final android.content.res.Resources getResources();
@@ -4858,11 +4858,11 @@
     method public void unregisterForContextMenu(android.view.View);
   }
 
-  public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+  public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
     ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
   }
 
-  public static class Fragment.SavedState implements android.os.Parcelable {
+  public static deprecated class Fragment.SavedState implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4881,17 +4881,17 @@
     method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
   }
 
-  public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+  public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
     method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
   }
 
-  public abstract class FragmentContainer {
+  public abstract deprecated class FragmentContainer {
     ctor public FragmentContainer();
     method public abstract <T extends android.view.View> T onFindViewById(int);
     method public abstract boolean onHasView();
   }
 
-  public class FragmentController {
+  public deprecated class FragmentController {
     method public void attachHost(android.app.Fragment);
     method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
     method public void dispatchActivityCreated();
@@ -4934,7 +4934,7 @@
     method public android.os.Parcelable saveAllState();
   }
 
-  public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+  public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
     ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
     method public void onAttachFragment(android.app.Fragment);
     method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4952,7 +4952,7 @@
     method public boolean onUseFragmentManagerInflaterFactory();
   }
 
-  public abstract class FragmentManager {
+  public abstract deprecated class FragmentManager {
     ctor public FragmentManager();
     method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
     method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4983,7 +4983,7 @@
     field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
   }
 
-  public static abstract interface FragmentManager.BackStackEntry {
+  public static abstract deprecated interface FragmentManager.BackStackEntry {
     method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
     method public abstract int getBreadCrumbShortTitleRes();
     method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4992,7 +4992,7 @@
     method public abstract java.lang.String getName();
   }
 
-  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+  public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
     ctor public FragmentManager.FragmentLifecycleCallbacks();
     method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
     method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -5010,14 +5010,14 @@
     method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
   }
 
-  public static abstract interface FragmentManager.OnBackStackChangedListener {
+  public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
     method public abstract void onBackStackChanged();
   }
 
-  public class FragmentManagerNonConfig {
+  public deprecated class FragmentManagerNonConfig {
   }
 
-  public abstract class FragmentTransaction {
+  public abstract deprecated class FragmentTransaction {
     ctor public FragmentTransaction();
     method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
     method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -5129,6 +5129,7 @@
     method public void setInTouchMode(boolean);
     method public void start();
     method public android.app.Activity startActivitySync(android.content.Intent);
+    method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
     method public deprecated void startAllocCounting();
     method public void startPerformanceSnapshot();
     method public void startProfiling();
@@ -5234,7 +5235,7 @@
     method public void setSelection(int);
   }
 
-  public class ListFragment extends android.app.Fragment {
+  public deprecated class ListFragment extends android.app.Fragment {
     ctor public ListFragment();
     method public android.widget.ListAdapter getListAdapter();
     method public android.widget.ListView getListView();
@@ -5248,7 +5249,7 @@
     method public void setSelection(int);
   }
 
-  public abstract class LoaderManager {
+  public abstract deprecated class LoaderManager {
     ctor public LoaderManager();
     method public abstract void destroyLoader(int);
     method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5258,7 +5259,7 @@
     method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
   }
 
-  public static abstract interface LoaderManager.LoaderCallbacks<D> {
+  public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
     method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
     method public abstract void onLoadFinished(android.content.Loader<D>, D);
     method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -6788,6 +6789,7 @@
     field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
     field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
     field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+    field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
     field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
     field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
     field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -7289,7 +7291,8 @@
     method public static final long getMinFlexMillis();
     method public long getMinLatencyMillis();
     method public static final long getMinPeriodMillis();
-    method public int getNetworkType();
+    method public deprecated int getNetworkType();
+    method public android.net.NetworkRequest getRequiredNetwork();
     method public android.content.ComponentName getService();
     method public android.os.Bundle getTransientExtras();
     method public long getTriggerContentMaxDelay();
@@ -7309,7 +7312,8 @@
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
     field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
-    field public static final int NETWORK_TYPE_METERED = 4; // 0x4
+    field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4
+    field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -7328,6 +7332,7 @@
     method public android.app.job.JobInfo.Builder setPeriodic(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
+    method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -9040,7 +9045,7 @@
     ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
   }
 
-  public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+  public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
     ctor public AsyncTaskLoader(android.content.Context);
     method public void cancelLoadInBackground();
     method public boolean isLoadInBackgroundCanceled();
@@ -9622,6 +9627,7 @@
     field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
     field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
     field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+    field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
     field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
     field public static final java.lang.String DISPLAY_SERVICE = "display";
     field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9801,7 +9807,7 @@
     method public void unregisterReceiver(android.content.BroadcastReceiver);
   }
 
-  public class CursorLoader extends android.content.AsyncTaskLoader {
+  public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
     ctor public CursorLoader(android.content.Context);
     ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public void deliverResult(android.database.Cursor);
@@ -10436,7 +10442,7 @@
     ctor public IntentSender.SendIntentException(java.lang.Exception);
   }
 
-  public class Loader<D> {
+  public deprecated class Loader<D> {
     ctor public Loader(android.content.Context);
     method public void abandon();
     method public boolean cancelLoad();
@@ -10469,15 +10475,15 @@
     method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
   }
 
-  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+  public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
     ctor public Loader.ForceLoadContentObserver();
   }
 
-  public static abstract interface Loader.OnLoadCanceledListener<D> {
+  public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
     method public abstract void onLoadCanceled(android.content.Loader<D>);
   }
 
-  public static abstract interface Loader.OnLoadCompleteListener<D> {
+  public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
     method public abstract void onLoadComplete(android.content.Loader<D>, D);
   }
 
@@ -11955,6 +11961,15 @@
 
 }
 
+package android.content.pm.crossprofile {
+
+  public class CrossProfileApps {
+    method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+    method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+  }
+
+}
+
 package android.content.pm.permission {
 
   public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
@@ -16018,6 +16033,7 @@
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+    method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
   }
 
   public static abstract class CameraCaptureSession.CaptureCallback {
@@ -16665,10 +16681,12 @@
     method public void addSurface(android.view.Surface);
     method public int describeContents();
     method public void enableSurfaceSharing();
+    method public static int getMaxSharedSurfaceCount();
     method public int getRotation();
     method public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
     method public java.util.List<android.view.Surface> getSurfaces();
+    method public void removeSurface(android.view.Surface);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int ROTATION_0 = 0; // 0x0
@@ -19831,11 +19849,13 @@
     method public synchronized void applyLocalizedPattern(java.lang.String);
     method public synchronized void applyPattern(java.lang.String);
     method public synchronized boolean areSignificantDigitsUsed();
+    method public synchronized boolean equals(java.lang.Object);
     method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
+    method public synchronized android.icu.util.Currency getCurrency();
     method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
     method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
     method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -19843,8 +19863,12 @@
     method public synchronized int getGroupingSize();
     method public synchronized java.math.MathContext getMathContext();
     method public synchronized android.icu.math.MathContext getMathContextICU();
+    method public synchronized int getMaximumFractionDigits();
+    method public synchronized int getMaximumIntegerDigits();
     method public synchronized int getMaximumSignificantDigits();
     method public synchronized byte getMinimumExponentDigits();
+    method public synchronized int getMinimumFractionDigits();
+    method public synchronized int getMinimumIntegerDigits();
     method public synchronized int getMinimumSignificantDigits();
     method public synchronized int getMultiplier();
     method public synchronized java.lang.String getNegativePrefix();
@@ -19855,13 +19879,19 @@
     method public synchronized java.lang.String getPositivePrefix();
     method public synchronized java.lang.String getPositiveSuffix();
     method public synchronized java.math.BigDecimal getRoundingIncrement();
+    method public synchronized int getRoundingMode();
     method public synchronized int getSecondaryGroupingSize();
+    method public synchronized int hashCode();
     method public synchronized boolean isDecimalPatternMatchRequired();
     method public synchronized boolean isDecimalSeparatorAlwaysShown();
     method public synchronized boolean isExponentSignAlwaysShown();
+    method public synchronized boolean isGroupingUsed();
     method public synchronized boolean isParseBigDecimal();
+    method public synchronized boolean isParseIntegerOnly();
+    method public synchronized boolean isParseStrict();
     method public synchronized boolean isScientificNotation();
     method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
+    method public synchronized void setCurrency(android.icu.util.Currency);
     method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
     method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
     method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -19870,10 +19900,15 @@
     method public synchronized void setExponentSignAlwaysShown(boolean);
     method public synchronized void setFormatWidth(int);
     method public synchronized void setGroupingSize(int);
+    method public synchronized void setGroupingUsed(boolean);
     method public synchronized void setMathContext(java.math.MathContext);
     method public synchronized void setMathContextICU(android.icu.math.MathContext);
+    method public synchronized void setMaximumFractionDigits(int);
+    method public synchronized void setMaximumIntegerDigits(int);
     method public synchronized void setMaximumSignificantDigits(int);
     method public synchronized void setMinimumExponentDigits(byte);
+    method public synchronized void setMinimumFractionDigits(int);
+    method public synchronized void setMinimumIntegerDigits(int);
     method public synchronized void setMinimumSignificantDigits(int);
     method public synchronized void setMultiplier(int);
     method public synchronized void setNegativePrefix(java.lang.String);
@@ -19881,12 +19916,15 @@
     method public synchronized void setPadCharacter(char);
     method public synchronized void setPadPosition(int);
     method public synchronized void setParseBigDecimal(boolean);
+    method public synchronized void setParseIntegerOnly(boolean);
     method public deprecated void setParseMaxDigits(int);
+    method public synchronized void setParseStrict(boolean);
     method public synchronized void setPositivePrefix(java.lang.String);
     method public synchronized void setPositiveSuffix(java.lang.String);
     method public synchronized void setRoundingIncrement(java.math.BigDecimal);
     method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
     method public synchronized void setRoundingIncrement(double);
+    method public synchronized void setRoundingMode(int);
     method public synchronized void setScientificNotation(boolean);
     method public synchronized void setSecondaryGroupingSize(int);
     method public synchronized void setSignificantDigitsUsed(boolean);
@@ -34605,6 +34643,14 @@
     method public static void setVmPolicy(android.os.StrictMode.VmPolicy);
   }
 
+  public static abstract interface StrictMode.OnThreadViolationListener {
+    method public abstract void onThreadViolation(android.os.strictmode.Violation);
+  }
+
+  public static abstract interface StrictMode.OnVmViolationListener {
+    method public abstract void onVmViolation(android.os.strictmode.Violation);
+  }
+
   public static final class StrictMode.ThreadPolicy {
     field public static final android.os.StrictMode.ThreadPolicy LAX;
   }
@@ -34625,6 +34671,7 @@
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+    method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
     method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
     method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -34656,6 +34703,7 @@
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+    method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
     method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
     method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
   }
@@ -34663,10 +34711,12 @@
   public final class SystemClock {
     method public static long currentThreadTimeMillis();
     method public static long elapsedRealtime();
+    method public static java.time.Clock elapsedRealtimeClock();
     method public static long elapsedRealtimeNanos();
     method public static boolean setCurrentTimeMillis(long);
     method public static void sleep(long);
     method public static long uptimeMillis();
+    method public static java.time.Clock uptimeMillisClock();
   }
 
   public class TestLooperManager {
@@ -34782,11 +34832,13 @@
     method public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(java.lang.String, android.os.UserHandle);
     method public android.os.Bundle getUserRestrictions();
     method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+    method public boolean hasRestrictedProfiles();
     method public boolean hasUserRestriction(java.lang.String);
     method public boolean isDemoUser();
     method public boolean isManagedProfile();
     method public boolean isManagedProfile(int);
     method public boolean isQuietModeEnabled(android.os.UserHandle);
+    method public boolean isRestrictedProfile();
     method public boolean isSystemUser();
     method public boolean isUserAGoat();
     method public boolean isUserRunning(android.os.UserHandle);
@@ -34811,6 +34863,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
     field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
     field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+    field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
     field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
     field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
     field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -34840,6 +34893,7 @@
     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";
+    field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
     field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
     field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
     field public static final int RESTRICTION_NOT_SET = 0; // 0x0
@@ -35099,6 +35153,62 @@
 
 }
 
+package android.os.strictmode {
+
+  public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class CustomViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class DiskReadViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class DiskWriteViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+  }
+
+  public class InstanceCountViolation extends android.os.strictmode.Violation {
+    method public long getNumberOfInstances();
+  }
+
+  public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class NetworkViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+  }
+
+  public abstract class Violation extends java.lang.Throwable {
+  }
+
+  public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+  }
+
+}
+
 package android.permissionpresenterservice {
 
   public abstract class RuntimePermissionPresenterService extends android.app.Service {
@@ -35397,7 +35507,7 @@
     method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
   }
 
-  public abstract class PreferenceFragment extends android.app.Fragment {
+  public abstract deprecated class PreferenceFragment extends android.app.Fragment {
     ctor public PreferenceFragment();
     method public void addPreferencesFromIntent(android.content.Intent);
     method public void addPreferencesFromResource(int);
@@ -35408,7 +35518,7 @@
     method public void setPreferenceScreen(android.preference.PreferenceScreen);
   }
 
-  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+  public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
     method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
   }
 
@@ -41730,6 +41840,16 @@
     field public final int errno;
   }
 
+  public class Int32Ref {
+    ctor public Int32Ref(int);
+    field public int value;
+  }
+
+  public class Int64Ref {
+    ctor public Int64Ref(long);
+    field public long value;
+  }
+
   public final class Os {
     method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -41799,7 +41919,8 @@
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
     method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
-    method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+    method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+    method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static void setegid(int) throws android.system.ErrnoException;
@@ -41824,7 +41945,8 @@
     method public static int umask(int);
     method public static android.system.StructUtsname uname();
     method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
-    method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+    method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+    method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
     method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -42545,7 +42667,9 @@
     ctor public CallAudioState(boolean, int, int);
     method public static java.lang.String audioRouteToString(int);
     method public int describeContents();
+    method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
     method public int getRoute();
+    method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
     method public int getSupportedRouteMask();
     method public boolean isMuted();
     method public void writeToParcel(android.os.Parcel, int);
@@ -42684,6 +42808,7 @@
     method public final void putExtras(android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
     method public final void removeExtras(java.lang.String...);
+    method public void requestBluetoothAudio(java.lang.String);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
@@ -42877,6 +43002,7 @@
     method public deprecated void onPhoneCreated(android.telecom.Phone);
     method public deprecated void onPhoneDestroyed(android.telecom.Phone);
     method public void onSilenceRinger();
+    method public final void requestBluetoothAudio(java.lang.String);
     method public final void setAudioRoute(int);
     method public final void setMuted(boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -43015,6 +43141,7 @@
     method public final android.telecom.CallAudioState getCallAudioState();
     method public final java.util.List<android.telecom.Call> getCalls();
     method public final void removeListener(android.telecom.Phone.Listener);
+    method public void requestBluetoothAudio(java.lang.String);
     method public final void setAudioRoute(int);
     method public final void setMuted(boolean);
   }
@@ -53788,7 +53915,7 @@
     method public abstract void setWebContentsDebuggingEnabled(boolean);
   }
 
-  public class WebViewFragment extends android.app.Fragment {
+  public deprecated class WebViewFragment extends android.app.Fragment {
     ctor public WebViewFragment();
     method public android.webkit.WebView getWebView();
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 40f732c..9f5ab62 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3611,11 +3611,11 @@
     method public android.transition.Scene getContentScene();
     method public android.transition.TransitionManager getContentTransitionManager();
     method public android.view.View getCurrentFocus();
-    method public android.app.FragmentManager getFragmentManager();
+    method public deprecated android.app.FragmentManager getFragmentManager();
     method public android.content.Intent getIntent();
     method public java.lang.Object getLastNonConfigurationInstance();
     method public android.view.LayoutInflater getLayoutInflater();
-    method public android.app.LoaderManager getLoaderManager();
+    method public deprecated android.app.LoaderManager getLoaderManager();
     method public java.lang.String getLocalClassName();
     method public int getMaxNumPictureInPictureActions();
     method public final android.media.session.MediaController getMediaController();
@@ -3655,7 +3655,7 @@
     method public void onActionModeStarted(android.view.ActionMode);
     method public void onActivityReenter(int, android.content.Intent);
     method protected void onActivityResult(int, int, android.content.Intent);
-    method public void onAttachFragment(android.app.Fragment);
+    method public deprecated void onAttachFragment(android.app.Fragment);
     method public void onAttachedToWindow();
     method public void onBackPressed();
     method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence);
@@ -3797,8 +3797,8 @@
     method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
     method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
     method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
-    method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
-    method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+    method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+    method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
     method public boolean startActivityIfNeeded(android.content.Intent, int);
     method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
     method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4484,7 +4484,7 @@
     method public void unregisterForContextMenu(android.view.View);
   }
 
-  public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+  public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
     ctor public DialogFragment();
     method public void dismiss();
     method public void dismissAllowingStateLoss();
@@ -4602,7 +4602,7 @@
     method public void setSelectedGroup(int);
   }
 
-  public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+  public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
     ctor public Fragment();
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final boolean equals(java.lang.Object);
@@ -4618,7 +4618,7 @@
     method public final java.lang.Object getHost();
     method public final int getId();
     method public final android.view.LayoutInflater getLayoutInflater();
-    method public android.app.LoaderManager getLoaderManager();
+    method public deprecated android.app.LoaderManager getLoaderManager();
     method public final android.app.Fragment getParentFragment();
     method public android.transition.Transition getReenterTransition();
     method public final android.content.res.Resources getResources();
@@ -4713,11 +4713,11 @@
     method public void unregisterForContextMenu(android.view.View);
   }
 
-  public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+  public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
     ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
   }
 
-  public static class Fragment.SavedState implements android.os.Parcelable {
+  public static deprecated class Fragment.SavedState implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4736,17 +4736,17 @@
     method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
   }
 
-  public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+  public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
     method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
   }
 
-  public abstract class FragmentContainer {
+  public abstract deprecated class FragmentContainer {
     ctor public FragmentContainer();
     method public abstract <T extends android.view.View> T onFindViewById(int);
     method public abstract boolean onHasView();
   }
 
-  public class FragmentController {
+  public deprecated class FragmentController {
     method public void attachHost(android.app.Fragment);
     method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
     method public void dispatchActivityCreated();
@@ -4789,7 +4789,7 @@
     method public android.os.Parcelable saveAllState();
   }
 
-  public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+  public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
     ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
     method public void onAttachFragment(android.app.Fragment);
     method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4807,7 +4807,7 @@
     method public boolean onUseFragmentManagerInflaterFactory();
   }
 
-  public abstract class FragmentManager {
+  public abstract deprecated class FragmentManager {
     ctor public FragmentManager();
     method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
     method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4838,7 +4838,7 @@
     field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
   }
 
-  public static abstract interface FragmentManager.BackStackEntry {
+  public static abstract deprecated interface FragmentManager.BackStackEntry {
     method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
     method public abstract int getBreadCrumbShortTitleRes();
     method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4847,7 +4847,7 @@
     method public abstract java.lang.String getName();
   }
 
-  public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+  public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
     ctor public FragmentManager.FragmentLifecycleCallbacks();
     method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
     method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -4865,14 +4865,14 @@
     method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
   }
 
-  public static abstract interface FragmentManager.OnBackStackChangedListener {
+  public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
     method public abstract void onBackStackChanged();
   }
 
-  public class FragmentManagerNonConfig {
+  public deprecated class FragmentManagerNonConfig {
   }
 
-  public abstract class FragmentTransaction {
+  public abstract deprecated class FragmentTransaction {
     ctor public FragmentTransaction();
     method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
     method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -4972,6 +4972,7 @@
     method public void setInTouchMode(boolean);
     method public void start();
     method public android.app.Activity startActivitySync(android.content.Intent);
+    method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
     method public deprecated void startAllocCounting();
     method public void startPerformanceSnapshot();
     method public void startProfiling();
@@ -5077,7 +5078,7 @@
     method public void setSelection(int);
   }
 
-  public class ListFragment extends android.app.Fragment {
+  public deprecated class ListFragment extends android.app.Fragment {
     ctor public ListFragment();
     method public android.widget.ListAdapter getListAdapter();
     method public android.widget.ListView getListView();
@@ -5091,7 +5092,7 @@
     method public void setSelection(int);
   }
 
-  public abstract class LoaderManager {
+  public abstract deprecated class LoaderManager {
     ctor public LoaderManager();
     method public abstract void destroyLoader(int);
     method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5101,7 +5102,7 @@
     method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
   }
 
-  public static abstract interface LoaderManager.LoaderCallbacks<D> {
+  public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
     method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
     method public abstract void onLoadFinished(android.content.Loader<D>, D);
     method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -6615,6 +6616,7 @@
     field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
     field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
     field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+    field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
     field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
     field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
     field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -6920,7 +6922,8 @@
     method public static final long getMinFlexMillis();
     method public long getMinLatencyMillis();
     method public static final long getMinPeriodMillis();
-    method public int getNetworkType();
+    method public deprecated int getNetworkType();
+    method public android.net.NetworkRequest getRequiredNetwork();
     method public android.content.ComponentName getService();
     method public android.os.Bundle getTransientExtras();
     method public long getTriggerContentMaxDelay();
@@ -6940,7 +6943,8 @@
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
     field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
-    field public static final int NETWORK_TYPE_METERED = 4; // 0x4
+    field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4
+    field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6959,6 +6963,7 @@
     method public android.app.job.JobInfo.Builder setPeriodic(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
+    method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -8603,7 +8608,7 @@
     ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
   }
 
-  public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+  public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
     ctor public AsyncTaskLoader(android.content.Context);
     method public void cancelLoadInBackground();
     method public boolean isLoadInBackgroundCanceled();
@@ -9178,6 +9183,7 @@
     field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
     field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
     field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+    field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
     field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
     field public static final java.lang.String DISPLAY_SERVICE = "display";
     field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9345,7 +9351,7 @@
     method public void unregisterReceiver(android.content.BroadcastReceiver);
   }
 
-  public class CursorLoader extends android.content.AsyncTaskLoader {
+  public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
     ctor public CursorLoader(android.content.Context);
     ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public void deliverResult(android.database.Cursor);
@@ -9951,7 +9957,7 @@
     ctor public IntentSender.SendIntentException(java.lang.Exception);
   }
 
-  public class Loader<D> {
+  public deprecated class Loader<D> {
     ctor public Loader(android.content.Context);
     method public void abandon();
     method public boolean cancelLoad();
@@ -9984,15 +9990,15 @@
     method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
   }
 
-  public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+  public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
     ctor public Loader.ForceLoadContentObserver();
   }
 
-  public static abstract interface Loader.OnLoadCanceledListener<D> {
+  public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
     method public abstract void onLoadCanceled(android.content.Loader<D>);
   }
 
-  public static abstract interface Loader.OnLoadCompleteListener<D> {
+  public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
     method public abstract void onLoadComplete(android.content.Loader<D>, D);
   }
 
@@ -11311,6 +11317,15 @@
 
 }
 
+package android.content.pm.crossprofile {
+
+  public class CrossProfileApps {
+    method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+    method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+  }
+
+}
+
 package android.content.res {
 
   public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -12308,6 +12323,7 @@
     method public static int getWALAutoCheckpoint();
     method public static int getWALConnectionPoolSize();
     method public static java.lang.String getWALSyncMode();
+    method public static boolean isCompatibilityWalSupported();
     method public static int releaseMemory();
   }
 
@@ -15404,6 +15420,7 @@
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+    method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
   }
 
   public static abstract class CameraCaptureSession.CaptureCallback {
@@ -16049,9 +16066,11 @@
     method public void addSurface(android.view.Surface);
     method public int describeContents();
     method public void enableSurfaceSharing();
+    method public static int getMaxSharedSurfaceCount();
     method public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
     method public java.util.List<android.view.Surface> getSurfaces();
+    method public void removeSurface(android.view.Surface);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -18417,11 +18436,13 @@
     method public synchronized void applyLocalizedPattern(java.lang.String);
     method public synchronized void applyPattern(java.lang.String);
     method public synchronized boolean areSignificantDigitsUsed();
+    method public synchronized boolean equals(java.lang.Object);
     method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
     method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
+    method public synchronized android.icu.util.Currency getCurrency();
     method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
     method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
     method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18429,8 +18450,12 @@
     method public synchronized int getGroupingSize();
     method public synchronized java.math.MathContext getMathContext();
     method public synchronized android.icu.math.MathContext getMathContextICU();
+    method public synchronized int getMaximumFractionDigits();
+    method public synchronized int getMaximumIntegerDigits();
     method public synchronized int getMaximumSignificantDigits();
     method public synchronized byte getMinimumExponentDigits();
+    method public synchronized int getMinimumFractionDigits();
+    method public synchronized int getMinimumIntegerDigits();
     method public synchronized int getMinimumSignificantDigits();
     method public synchronized int getMultiplier();
     method public synchronized java.lang.String getNegativePrefix();
@@ -18441,13 +18466,19 @@
     method public synchronized java.lang.String getPositivePrefix();
     method public synchronized java.lang.String getPositiveSuffix();
     method public synchronized java.math.BigDecimal getRoundingIncrement();
+    method public synchronized int getRoundingMode();
     method public synchronized int getSecondaryGroupingSize();
+    method public synchronized int hashCode();
     method public synchronized boolean isDecimalPatternMatchRequired();
     method public synchronized boolean isDecimalSeparatorAlwaysShown();
     method public synchronized boolean isExponentSignAlwaysShown();
+    method public synchronized boolean isGroupingUsed();
     method public synchronized boolean isParseBigDecimal();
+    method public synchronized boolean isParseIntegerOnly();
+    method public synchronized boolean isParseStrict();
     method public synchronized boolean isScientificNotation();
     method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
+    method public synchronized void setCurrency(android.icu.util.Currency);
     method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
     method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
     method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18456,10 +18487,15 @@
     method public synchronized void setExponentSignAlwaysShown(boolean);
     method public synchronized void setFormatWidth(int);
     method public synchronized void setGroupingSize(int);
+    method public synchronized void setGroupingUsed(boolean);
     method public synchronized void setMathContext(java.math.MathContext);
     method public synchronized void setMathContextICU(android.icu.math.MathContext);
+    method public synchronized void setMaximumFractionDigits(int);
+    method public synchronized void setMaximumIntegerDigits(int);
     method public synchronized void setMaximumSignificantDigits(int);
     method public synchronized void setMinimumExponentDigits(byte);
+    method public synchronized void setMinimumFractionDigits(int);
+    method public synchronized void setMinimumIntegerDigits(int);
     method public synchronized void setMinimumSignificantDigits(int);
     method public synchronized void setMultiplier(int);
     method public synchronized void setNegativePrefix(java.lang.String);
@@ -18467,12 +18503,15 @@
     method public synchronized void setPadCharacter(char);
     method public synchronized void setPadPosition(int);
     method public synchronized void setParseBigDecimal(boolean);
+    method public synchronized void setParseIntegerOnly(boolean);
     method public deprecated void setParseMaxDigits(int);
+    method public synchronized void setParseStrict(boolean);
     method public synchronized void setPositivePrefix(java.lang.String);
     method public synchronized void setPositiveSuffix(java.lang.String);
     method public synchronized void setRoundingIncrement(java.math.BigDecimal);
     method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
     method public synchronized void setRoundingIncrement(double);
+    method public synchronized void setRoundingMode(int);
     method public synchronized void setScientificNotation(boolean);
     method public synchronized void setSecondaryGroupingSize(int);
     method public synchronized void setSignificantDigitsUsed(boolean);
@@ -32057,6 +32096,14 @@
     field public static final int DETECT_VM_UNTAGGED_SOCKET = -2147483648; // 0x80000000
   }
 
+  public static abstract interface StrictMode.OnThreadViolationListener {
+    method public abstract void onThreadViolation(android.os.strictmode.Violation);
+  }
+
+  public static abstract interface StrictMode.OnVmViolationListener {
+    method public abstract void onVmViolation(android.os.strictmode.Violation);
+  }
+
   public static final class StrictMode.ThreadPolicy {
     field public static final android.os.StrictMode.ThreadPolicy LAX;
   }
@@ -32077,6 +32124,7 @@
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+    method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
     method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
     method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -32132,6 +32180,7 @@
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+    method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
     method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
     method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
   }
@@ -32139,10 +32188,12 @@
   public final class SystemClock {
     method public static long currentThreadTimeMillis();
     method public static long elapsedRealtime();
+    method public static java.time.Clock elapsedRealtimeClock();
     method public static long elapsedRealtimeNanos();
     method public static boolean setCurrentTimeMillis(long);
     method public static void sleep(long);
     method public static long uptimeMillis();
+    method public static java.time.Clock uptimeMillisClock();
   }
 
   public class TestLooperManager {
@@ -32227,6 +32278,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
     field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
     field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+    field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
     field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
     field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
     field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -32255,6 +32307,7 @@
     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";
+    field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
     field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
     field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
     field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
@@ -32495,6 +32548,62 @@
 
 }
 
+package android.os.strictmode {
+
+  public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class CustomViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class DiskReadViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class DiskWriteViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+  }
+
+  public class InstanceCountViolation extends android.os.strictmode.Violation {
+    method public long getNumberOfInstances();
+  }
+
+  public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class NetworkViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+  }
+
+  public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+  }
+
+  public abstract class Violation extends java.lang.Throwable {
+  }
+
+  public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+  }
+
+}
+
 package android.preference {
 
   public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -32781,7 +32890,7 @@
     method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
   }
 
-  public abstract class PreferenceFragment extends android.app.Fragment {
+  public abstract deprecated class PreferenceFragment extends android.app.Fragment {
     ctor public PreferenceFragment();
     method public void addPreferencesFromIntent(android.content.Intent);
     method public void addPreferencesFromResource(int);
@@ -32792,7 +32901,7 @@
     method public void setPreferenceScreen(android.preference.PreferenceScreen);
   }
 
-  public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+  public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
     method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
   }
 
@@ -35545,6 +35654,7 @@
     field public static final java.lang.String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins";
     field public static final deprecated java.lang.String ALLOW_MOCK_LOCATION = "mock_location";
     field public static final java.lang.String ANDROID_ID = "android_id";
+    field public static final java.lang.String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
     field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
     field public static final deprecated java.lang.String BACKGROUND_DATA = "background_data";
     field public static final deprecated java.lang.String BLUETOOTH_ON = "bluetooth_on";
@@ -37600,6 +37710,13 @@
     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 FieldsDetection implements android.os.Parcelable {
+    ctor public FieldsDetection(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldsDetection> CREATOR;
+  }
+
   public final class FillCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess(android.service.autofill.FillResponse);
@@ -37625,6 +37742,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<java.lang.String, java.lang.Integer> getDetectedFields();
     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();
@@ -37662,6 +37780,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 setFieldsDetection(android.service.autofill.FieldsDetection);
     method public android.service.autofill.FillResponse.Builder setFlags(int);
     method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
@@ -38809,6 +38928,16 @@
     field public final int errno;
   }
 
+  public class Int32Ref {
+    ctor public Int32Ref(int);
+    field public int value;
+  }
+
+  public class Int64Ref {
+    ctor public Int64Ref(long);
+    field public long value;
+  }
+
   public final class Os {
     method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -38878,7 +39007,8 @@
     method public static void remove(java.lang.String) throws android.system.ErrnoException;
     method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
-    method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+    method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+    method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static void setegid(int) throws android.system.ErrnoException;
@@ -38903,7 +39033,8 @@
     method public static int umask(int);
     method public static android.system.StructUtsname uname();
     method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
-    method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+    method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+    method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
     method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39599,7 +39730,9 @@
     ctor public CallAudioState(boolean, int, int);
     method public static java.lang.String audioRouteToString(int);
     method public int describeContents();
+    method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
     method public int getRoute();
+    method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
     method public int getSupportedRouteMask();
     method public boolean isMuted();
     method public void writeToParcel(android.os.Parcel, int);
@@ -39734,6 +39867,7 @@
     method public final void putExtras(android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
     method public final void removeExtras(java.lang.String...);
+    method public void requestBluetoothAudio(java.lang.String);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
     method public final void sendRemoteRttRequest();
     method public final void sendRttInitiationFailure(int);
@@ -39937,6 +40071,7 @@
     method public void onCanAddCallChanged(boolean);
     method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
     method public void onSilenceRinger();
+    method public final void requestBluetoothAudio(java.lang.String);
     method public final void setAudioRoute(int);
     method public final void setMuted(boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -41326,6 +41461,7 @@
   }
 
   public final class FileInfo implements android.os.Parcelable {
+    ctor public FileInfo(android.net.Uri, java.lang.String);
     method public int describeContents();
     method public java.lang.String getMimeType();
     method public android.net.Uri getUri();
@@ -41439,6 +41575,14 @@
     field public static final android.os.Parcelable.Creator<android.telephony.mbms.StreamingServiceInfo> CREATOR;
   }
 
+  public final class UriPathPair implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.net.Uri getContentUri();
+    method public android.net.Uri getFilePathUri();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.mbms.UriPathPair> CREATOR;
+  }
+
 }
 
 package android.telephony.mbms.vendor {
@@ -41470,6 +41614,23 @@
     method public void stopStreaming(int, java.lang.String) throws android.os.RemoteException;
   }
 
+  public class VendorUtils {
+    ctor public VendorUtils();
+    method public static android.content.ComponentName getAppReceiverFromPackageName(android.content.Context, java.lang.String);
+    field public static final java.lang.String ACTION_CLEANUP = "android.telephony.mbms.action.CLEANUP";
+    field public static final java.lang.String ACTION_DOWNLOAD_RESULT_INTERNAL = "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+    field public static final java.lang.String ACTION_FILE_DESCRIPTOR_REQUEST = "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+    field public static final java.lang.String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+    field public static final java.lang.String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+    field public static final java.lang.String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+    field public static final java.lang.String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+    field public static final java.lang.String EXTRA_PAUSED_URI_LIST = "android.telephony.mbms.extra.PAUSED_URI_LIST";
+    field public static final java.lang.String EXTRA_SERVICE_ID = "android.telephony.mbms.extra.SERVICE_ID";
+    field public static final java.lang.String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+    field public static final java.lang.String EXTRA_TEMP_FILE_ROOT = "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+    field public static final java.lang.String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+  }
+
 }
 
 package android.test {
@@ -50500,7 +50661,7 @@
     method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
   }
 
-  public class WebViewFragment extends android.app.Fragment {
+  public deprecated class WebViewFragment extends android.app.Fragment {
     ctor public WebViewFragment();
     method public android.webkit.WebView getWebView();
   }
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 93b9f58..d79b1a6 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -47,6 +47,16 @@
 
 /**
  * Runs the am instrument command
+ *
+ * Test Result Code:
+ * 1 - Test running
+ * 0 - Test passed
+ * -2 - assertion failure
+ * -1 - other exceptions
+ *
+ * Session Result Code:
+ * -1: Success
+ * other: Failure
  */
 public class Instrument {
     public static final String DEFAULT_LOG_DIR = "instrument-logs";
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index c7d1ca2..0b51e66 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -22,19 +22,28 @@
 #include <sstream>
 #include <unistd.h>
 
-const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB
+bool isValidChar(char c) {
+    uint8_t v = (uint8_t)c;
+    return (v >= (uint8_t)'a' && v <= (uint8_t)'z')
+        || (v >= (uint8_t)'A' && v <= (uint8_t)'Z')
+        || (v >= (uint8_t)'0' && v <= (uint8_t)'9')
+        || (v == (uint8_t)'_');
+}
 
-
-static std::string trim(const std::string& s) {
-    const auto head = s.find_first_not_of(DEFAULT_WHITESPACE);
+static std::string trim(const std::string& s, const std::string& chars) {
+    const auto head = s.find_first_not_of(chars);
     if (head == std::string::npos) return "";
 
-    const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE);
+    const auto tail = s.find_last_not_of(chars);
     return s.substr(head, tail - head + 1);
 }
 
+static std::string trimDefault(const std::string& s) {
+    return trim(s, DEFAULT_WHITESPACE);
+}
+
 static std::string trimHeader(const std::string& s) {
-    std::string res = trim(s);
+    std::string res = trimDefault(s);
     std::transform(res.begin(), res.end(), res.begin(), ::tolower);
     return res;
 }
@@ -68,22 +77,68 @@
 
 record_t parseRecord(const std::string& line, const std::string& delimiters) {
     record_t record;
-    trans_func f = &trim;
+    trans_func f = &trimDefault;
     split(line, record, f, delimiters);
     return record;
 }
 
-bool hasPrefix(std::string* line, const char* key) {
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
+    record_t record;
+    int lastIndex = 0;
+    int lineSize = (int)line.size();
+    for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
+        int idx = *it;
+        if (lastIndex > idx || idx > lineSize) {
+            record.clear(); // The indices is wrong, return empty;
+            return record;
+        }
+        while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
+        record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
+        lastIndex = idx;
+    }
+    record.push_back(trimDefault(line.substr(lastIndex, lineSize - lastIndex)));
+    return record;
+}
+
+bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
     const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
     if (head == std::string::npos) return false;
-    auto i = 0;
-    auto j = head;
+    int len = (int)line->length();
+    int i = 0;
+    int j = head;
     while (key[i] != '\0') {
-        if (j >= line->size() || key[i++] != line->at(j++)) {
+        if (j >= len || key[i++] != line->at(j++)) {
             return false;
         }
     }
-    line->assign(trim(line->substr(j)));
+
+    if (endAtDelimiter) {
+        // this means if the line only have prefix or no delimiter, we still return false.
+        if (j == len || isValidChar(line->at(j))) return false;
+    }
+
+    line->assign(trimDefault(line->substr(j)));
+    return true;
+}
+
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
+    const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE);
+    if (tail == std::string::npos) return false;
+    int i = 0;
+    while (key[++i] != '\0'); // compute the size of the key
+    int j = tail;
+    while (i > 0) {
+        if (j < 0 || key[--i] != line->at(j--)) {
+            return false;
+        }
+    }
+
+    if (endAtDelimiter) {
+        // this means if the line only have suffix or no delimiter, we still return false.
+        if (j < 0 || isValidChar(line->at(j))) return false;
+    }
+
+    line->assign(trimDefault(line->substr(0, j+1)));
     return true;
 }
 
@@ -95,65 +150,36 @@
     return atoll(s.c_str());
 }
 
-// ==============================================================================
-Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
+double toDouble(const std::string& s) {
+    return atof(s.c_str());
+}
 
-Reader::Reader(const int fd, const size_t capacity)
-        : mFd(fd), mMaxSize(capacity), mBufSize(0), mRead(0), mFlushed(0)
+// ==============================================================================
+Reader::Reader(const int fd)
 {
-    mBuf = capacity > 0 ? (char*)malloc(capacity * sizeof(char)) : NULL;
-    mStatus = mFd < 0 ? "Negative fd" : (capacity == 0 ? "Zero buffer capacity" : "");
+    mFile = fdopen(fd, "r");
+    mStatus = mFile == NULL ? "Invalid fd " + std::to_string(fd) : "";
 }
 
 Reader::~Reader()
 {
-    free(mBuf);
+    if (mFile != NULL) fclose(mFile);
 }
 
-bool Reader::readLine(std::string* line, const char newline) {
-    if (!ok(line)) return false; // bad status
-    line->clear();
-    std::stringstream ss;
-    while (!EOR()) {
-        // read if available
-        if (mFd != -1 && mBufSize != mMaxSize) {
-            ssize_t amt = 0;
-            if (mRead >= mFlushed) {
-                amt = ::read(mFd, mBuf + mRead, mMaxSize - mRead);
-            } else {
-                amt = ::read(mFd, mBuf + mRead, mFlushed - mRead);
-            }
-            if (amt < 0) {
-                mStatus = "Fail to read from fd";
-                return false;
-            } else if (amt == 0) {
-                close(mFd);
-                mFd = -1;
-            }
-            mRead += amt;
-            mBufSize += amt;
-        }
+bool Reader::readLine(std::string* line) {
+    if (mFile == NULL) return false;
 
-        bool meetsNewLine = false;
-        if (mBufSize > 0) {
-            int start = mFlushed;
-            int end = mFlushed < mRead ? mRead : mMaxSize;
-            while (mFlushed < end && mBuf[mFlushed++] != newline && mBufSize > 0) mBufSize--;
-            meetsNewLine = (mBuf[mFlushed-1] == newline);
-            if (meetsNewLine) mBufSize--; // deduct the new line character
-            size_t len = meetsNewLine ? mFlushed - start - 1 : mFlushed - start;
-            ss.write(mBuf + start, len);
-        }
-
-        if (mRead >= (int) mMaxSize) mRead = 0;
-        if (mFlushed >= (int) mMaxSize) mFlushed = 0;
-
-        if (EOR() || meetsNewLine) {
-            line->assign(ss.str());
-            return true;
-        }
+    char* buf = NULL;
+    size_t len = 0;
+    ssize_t read = getline(&buf, &len, mFile);
+    if (read != -1) {
+        std::string s(buf);
+        line->assign(trim(s, DEFAULT_NEWLINE));
+    } else if (errno == EINVAL) {
+        mStatus = "Bad Argument";
     }
-    return false;
+    free(buf);
+    return read != -1;
 }
 
 bool Reader::ok(std::string* error) {
@@ -162,10 +188,41 @@
 }
 
 // ==============================================================================
+static int
+lookupName(const char** names, const int size, const char* name)
+{
+    for (int i=0; i<size; i++) {
+        if (strcmp(name, names[i]) == 0) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+EnumTypeMap::EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount)
+        :mEnumNames(enumNames),
+         mEnumValues(enumValues),
+         mEnumCount(enumCount)
+{
+}
+
+EnumTypeMap::~EnumTypeMap()
+{
+}
+
+int
+EnumTypeMap::parseValue(const std::string& value)
+{
+    int index = lookupName(mEnumNames, mEnumCount, value.c_str());
+    if (index < 0) return mEnumValues[0]; // Assume value 0 is default
+    return mEnumValues[index];
+}
+
 Table::Table(const char* names[], const uint64_t ids[], const int count)
         :mFieldNames(names),
          mFieldIds(ids),
-         mFieldCount(count)
+         mFieldCount(count),
+         mEnums()
 {
 }
 
@@ -173,41 +230,54 @@
 {
 }
 
-bool
-Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value)
+void
+Table::addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize)
 {
-    uint64_t found = 0;
-    for (int i=0; i<mFieldCount; i++) {
-        if (strcmp(name.c_str(), mFieldNames[i]) == 0) {
-            found = mFieldIds[i];
-            break;
-        }
-    }
+    int index = lookupName(mFieldNames, mFieldCount, field);
+    if (index < 0) return;
 
+    EnumTypeMap enu(enumNames, enumValues, enumSize);
+    mEnums[index] = enu;
+}
+
+bool
+Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
+{
+    int index = lookupName(mFieldNames, mFieldCount, name.c_str());
+    if (index < 0) return false;
+
+    uint64_t found = mFieldIds[index];
     switch (found & FIELD_TYPE_MASK) {
         case FIELD_TYPE_DOUBLE:
         case FIELD_TYPE_FLOAT:
-            // TODO: support parse string to float/double
-            return false;
+            proto->write(found, toDouble(value));
+            break;
         case FIELD_TYPE_STRING:
         case FIELD_TYPE_BYTES:
-            proto.write(found, value);
+            proto->write(found, value);
             break;
         case FIELD_TYPE_INT64:
         case FIELD_TYPE_SINT64:
         case FIELD_TYPE_UINT64:
         case FIELD_TYPE_FIXED64:
         case FIELD_TYPE_SFIXED64:
-            proto.write(found, toLongLong(value));
+            proto->write(found, toLongLong(value));
             break;
         case FIELD_TYPE_BOOL:
+            return false;
         case FIELD_TYPE_ENUM:
+            if (mEnums.find(index) == mEnums.end()) {
+                // forget to add enum type mapping
+                return false;
+            }
+            proto->write(found, mEnums[index].parseValue(value));
+            break;
         case FIELD_TYPE_INT32:
         case FIELD_TYPE_SINT32:
         case FIELD_TYPE_UINT32:
         case FIELD_TYPE_FIXED32:
         case FIELD_TYPE_SFIXED32:
-            proto.write(found, toInt(value));
+            proto->write(found, toInt(value));
             break;
         default:
             return false;
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 86761e9..e8366fa 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -17,9 +17,9 @@
 #ifndef INCIDENT_HELPER_UTIL_H
 #define INCIDENT_HELPER_UTIL_H
 
+#include <map>
 #include <string>
 #include <vector>
-#include <sstream>
 
 #include <android/util/ProtoOutputStream.h>
 
@@ -29,8 +29,13 @@
 typedef std::vector<std::string> record_t;
 typedef std::string (*trans_func) (const std::string&);
 
-const char DEFAULT_NEWLINE = '\n';
 const std::string DEFAULT_WHITESPACE = " \t";
+const std::string DEFAULT_NEWLINE = "\r\n";
+const std::string TAB_DELIMITER = "\t";
+const std::string COMMA_DELIMITER = ",";
+
+// returns true if c is a-zA-Z0-9 or underscore _
+bool isValidChar(char c);
 
 /**
  * When a text has a table format like this
@@ -47,19 +52,33 @@
 record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
 
 /**
- * When the line starts with the given key, the function returns true
- * as well as the line argument is changed to the rest part of the original.
+ * When a text-format table aligns by its vertical position, it is not possible to split them by purely delimiters.
+ * This function allows to parse record by its header's column position' indices, must in ascending order.
+ * At the same time, it still looks at the char at index, if it doesn't belong to delimiters, moves forward to find the delimiters.
+ */
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters = DEFAULT_WHITESPACE);
+
+/**
+ * When the line starts/ends with the given key, the function returns true
+ * as well as the line argument is changed to the rest trimmed part of the original.
  * e.g. "ZRAM: 6828K physical used for 31076K in swap (524284K total swap)" becomes
  * "6828K physical used for 31076K in swap (524284K total swap)" when given key "ZRAM:",
  * otherwise the line is not changed.
+ *
+ * In order to prevent two values have same prefix which cause entering to incorrect conditions,
+ * stripPrefix and stripSuffix can turn on a flag that requires the ending char in the line must not be a valid
+ * character or digits, this feature is off by default.
+ * i.e. ABC%some value, ABCD%other value
  */
-bool hasPrefix(std::string* line, const char* key);
+bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false);
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false);
 
 /**
  * Converts string to the desired type
  */
 int toInt(const std::string& s);
 long long toLongLong(const std::string& s);
+double toDouble(const std::string& s);
 
 /**
  * Reader class reads data from given fd in streaming fashion.
@@ -69,23 +88,29 @@
 {
 public:
     Reader(const int fd);
-    Reader(const int fd, const size_t capacity);
     ~Reader();
 
-    bool readLine(std::string* line, const char newline = DEFAULT_NEWLINE);
+    bool readLine(std::string* line);
     bool ok(std::string* error);
 
 private:
-    int mFd; // set mFd to -1 when read EOF()
-    const size_t mMaxSize;
-    size_t mBufSize;
-    char* mBuf; // implements a circular buffer
-
-    int mRead;
-    int mFlushed;
+    FILE* mFile;
     std::string mStatus;
-    // end of read
-    inline bool EOR() { return mFd == -1 && mBufSize == 0; };
+};
+
+class EnumTypeMap
+{
+public:
+    EnumTypeMap() {};
+    EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount);
+    ~EnumTypeMap();
+
+    int parseValue(const std::string& value);
+
+private:
+    const char** mEnumNames;
+    const uint32_t* mEnumValues;
+    int mEnumCount;
 };
 
 /**
@@ -98,12 +123,15 @@
     Table(const char* names[], const uint64_t ids[], const int count);
     ~Table();
 
-    bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value);
+    // Add enum names to values for parsing purpose.
+    void addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize);
 
+    bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
 private:
     const char** mFieldNames;
     const uint64_t* mFieldIds;
     const int mFieldCount;
+    map<int, EnumTypeMap> mEnums;
 };
 
 #endif  // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 3da87b9c..5ebe9bd 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "incident_helper"
 
+#include "parsers/CpuInfoParser.h"
 #include "parsers/KernelWakesParser.h"
 #include "parsers/PageTypeInfoParser.h"
 #include "parsers/ProcrankParser.h"
@@ -54,6 +55,8 @@
             return new PageTypeInfoParser();
         case 2002:
             return new KernelWakesParser();
+        case 2003:
+            return new CpuInfoParser();
         default:
             return NULL;
     }
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
new file mode 100644
index 0000000..3faca00
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/cpuinfo.proto.h"
+#include "ih_util.h"
+#include "CpuInfoParser.h"
+
+using namespace android::os;
+
+static void writeSuffixLine(ProtoOutputStream* proto, uint64_t fieldId,
+        const string& line, const string& delimiter,
+        const int count, const char* names[], const uint64_t ids[])
+{
+    record_t record = parseRecord(line, delimiter);
+    long long token = proto->start(fieldId);
+    for (int i=0; i<(int)record.size(); i++) {
+        for (int j=0; j<count; j++) {
+            if (stripSuffix(&record[i], names[j], true)) {
+                proto->write(ids[j], toInt(record[i]));
+                break;
+            }
+        }
+    }
+    proto->end(token);
+}
+
+status_t
+CpuInfoParser::Parse(const int in, const int out) const
+{
+    Reader reader(in);
+    string line;
+    header_t header;
+    vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
+    record_t record;
+    int nline = 0;
+    bool nextToSwap = false;
+    bool nextToUsage = false;
+
+    ProtoOutputStream proto;
+    Table table(CpuInfo::Task::_FIELD_NAMES, CpuInfo::Task::_FIELD_IDS, CpuInfo::Task::_FIELD_COUNT);
+    table.addEnumTypeMap("s", CpuInfo::Task::_ENUM_STATUS_NAMES,
+            CpuInfo::Task::_ENUM_STATUS_VALUES, CpuInfo::Task::_ENUM_STATUS_COUNT);
+    table.addEnumTypeMap("pcy", CpuInfo::Task::_ENUM_POLICY_NAMES,
+            CpuInfo::Task::_ENUM_POLICY_VALUES, CpuInfo::Task::_ENUM_POLICY_COUNT);
+
+    // parse line by line
+    while (reader.readLine(&line)) {
+        if (line.empty()) continue;
+
+        nline++;
+
+        if (stripPrefix(&line, "Tasks:")) {
+            writeSuffixLine(&proto, CpuInfo::TASK_STATS, line, COMMA_DELIMITER,
+                CpuInfo::TaskStats::_FIELD_COUNT,
+                CpuInfo::TaskStats::_FIELD_NAMES,
+                CpuInfo::TaskStats::_FIELD_IDS);
+            continue;
+        }
+        if (stripPrefix(&line, "Mem:")) {
+            writeSuffixLine(&proto, CpuInfo::MEM, line, COMMA_DELIMITER,
+                CpuInfo::MemStats::_FIELD_COUNT,
+                CpuInfo::MemStats::_FIELD_NAMES,
+                CpuInfo::MemStats::_FIELD_IDS);
+            continue;
+        }
+        if (stripPrefix(&line, "Swap:")) {
+            writeSuffixLine(&proto, CpuInfo::SWAP, line, COMMA_DELIMITER,
+                CpuInfo::MemStats::_FIELD_COUNT,
+                CpuInfo::MemStats::_FIELD_NAMES,
+                CpuInfo::MemStats::_FIELD_IDS);
+            nextToSwap = true;
+            continue;
+        }
+
+        if (nextToSwap) {
+            writeSuffixLine(&proto, CpuInfo::CPU_USAGE, line, DEFAULT_WHITESPACE,
+                CpuInfo::CpuUsage::_FIELD_COUNT,
+                CpuInfo::CpuUsage::_FIELD_NAMES,
+                CpuInfo::CpuUsage::_FIELD_IDS);
+            nextToUsage = true;
+            nextToSwap = false;
+            continue;
+        }
+
+        // Header of tasks must be next to usage line
+        if (nextToUsage) {
+            // How to parse Header of Tasks:
+            // PID   TID USER         PR  NI[%CPU]S VIRT  RES PCY CMD             NAME
+            // After parsing, header = { PID, TID, USER, PR, NI, CPU, S, VIRT, RES, PCY, CMD, NAME }
+            // And columnIndices will contain end index of each word.
+            header = parseHeader(line, "[ %]");
+            nextToUsage = false;
+
+            // NAME is not in the list since the last split index is default to the end of line.
+            const char* headerNames[11] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD" };
+            size_t lastIndex = 0;
+            for (int i = 0; i < 11; i++) {
+                string s = headerNames[i];
+                lastIndex = line.find(s, lastIndex);
+                if (lastIndex == string::npos) {
+                    fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
+                    return -1;
+                }
+                lastIndex += s.length();
+                columnIndices.push_back(lastIndex);
+            }
+            // Need to remove the end index of CMD and use the start index of NAME because CMD values contain spaces.
+            // for example: ... CMD             NAME
+            //              ... Jit thread pool com.google.android.gms.feedback
+            // If use end index of CMD, parsed result = { "Jit", "thread pool com.google.android.gms.feedback" }
+            // If use start index of NAME, parsed result = { "Jit thread pool", "com.google.android.gms.feedback" }
+            int endCMD = columnIndices.back();
+            columnIndices.pop_back();
+            columnIndices.push_back(line.find("NAME", endCMD) - 1);
+            continue;
+        }
+
+        record = parseRecordByColumns(line, columnIndices);
+        if (record.size() != header.size()) {
+            fprintf(stderr, "[%s]Line %d has missing fields:\n%s\n", this->name.string(), nline, line.c_str());
+            continue;
+        }
+
+        long long token = proto.start(CpuInfo::TASKS);
+        for (int i=0; i<(int)record.size(); i++) {
+            if (!table.insertField(&proto, header[i], record[i])) {
+                fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
+                        this->name.string(), nline, header[i].c_str(), record[i].c_str());
+            }
+        }
+        proto.end(token);
+    }
+
+    if (!reader.ok(&line)) {
+        fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+        return -1;
+    }
+
+    if (!proto.flush(out)) {
+        fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+        return -1;
+    }
+    fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+    return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.h b/cmds/incident_helper/src/parsers/CpuInfoParser.h
new file mode 100644
index 0000000..f57bb4e
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CPU_INFO_PARSER_H
+#define CPU_INFO_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu info parser, parses text produced by command
+ * 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
+ */
+class CpuInfoParser : public TextParserBase {
+public:
+    CpuInfoParser() : TextParserBase(String8("CpuInfoParser")) {};
+    ~CpuInfoParser() {};
+
+    virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif  // CPU_INFO_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
index cc4a1e1..ada4a5d 100644
--- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -23,8 +23,6 @@
 
 using namespace android::os;
 
-const std::string LINE_DELIMITER = "\t";
-
 status_t
 KernelWakesParser::Parse(const int in, const int out) const
 {
@@ -42,12 +40,12 @@
         if (line.empty()) continue;
         // parse head line
         if (nline++ == 0) {
-            header = parseHeader(line, LINE_DELIMITER);
+            header = parseHeader(line, TAB_DELIMITER);
             continue;
         }
 
         // parse for each record, the line delimiter is \t only!
-        record = parseRecord(line, LINE_DELIMITER);
+        record = parseRecord(line, TAB_DELIMITER);
 
         if (record.size() != header.size()) {
             // TODO: log this to incident report!
@@ -57,7 +55,7 @@
 
         long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
         for (int i=0; i<(int)record.size(); i++) {
-            if (!table.insertField(proto, header[i], record[i])) {
+            if (!table.insertField(&proto, header[i], record[i])) {
                 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
                         this->name.string(), nline, header[i].c_str(), record[i].c_str());
             }
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index 6047bd1..f1b93ff 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -23,8 +23,6 @@
 
 using namespace android::os;
 
-const std::string LINE_DELIMITER = ",";
-
 status_t
 PageTypeInfoParser::Parse(const int in, const int out) const
 {
@@ -44,37 +42,37 @@
             continue;
         }
 
-        if (hasPrefix(&line, "Page block order:")) {
+        if (stripPrefix(&line, "Page block order:")) {
             pageBlockOrder = toInt(line);
             proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
             continue;
         }
-        if (hasPrefix(&line, "Pages per block:")) {
+        if (stripPrefix(&line, "Pages per block:")) {
             proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
             continue;
         }
-        if (hasPrefix(&line, "Free pages count per migrate type at order")) {
+        if (stripPrefix(&line, "Free pages count per migrate type at order")) {
             migrateTypeSession = true;
             continue;
         }
-        if (hasPrefix(&line, "Number of blocks type")) {
+        if (stripPrefix(&line, "Number of blocks type")) {
             blockHeader = parseHeader(line);
             continue;
         }
 
-        record_t record = parseRecord(line, LINE_DELIMITER);
+        record_t record = parseRecord(line, COMMA_DELIMITER);
         if (migrateTypeSession && record.size() == 3) {
             long long token = proto.start(PageTypeInfo::MIGRATE_TYPES);
             // expect part 0 starts with "Node"
-            if (hasPrefix(&record[0], "Node")) {
+            if (stripPrefix(&record[0], "Node")) {
                 proto.write(MigrateTypeProto::NODE, toInt(record[0]));
             } else return BAD_VALUE;
             // expect part 1 starts with "zone"
-            if (hasPrefix(&record[1], "zone")) {
+            if (stripPrefix(&record[1], "zone")) {
                 proto.write(MigrateTypeProto::ZONE, record[1]);
             } else return BAD_VALUE;
             // expect part 2 starts with "type"
-            if (hasPrefix(&record[2], "type")) {
+            if (stripPrefix(&record[2], "type")) {
                 // expect the rest of part 2 has number of (pageBlockOrder + 2) parts
                 // An example looks like:
                 // header line:      type    0   1   2 3 4 5 6 7 8 9 10
@@ -94,16 +92,16 @@
             proto.end(token);
         } else if (!blockHeader.empty() && record.size() == 2) {
             long long token = proto.start(PageTypeInfo::BLOCKS);
-            if (hasPrefix(&record[0], "Node")) {
+            if (stripPrefix(&record[0], "Node")) {
                 proto.write(BlockProto::NODE, toInt(record[0]));
             } else return BAD_VALUE;
 
-            if (hasPrefix(&record[1], "zone")) {
+            if (stripPrefix(&record[1], "zone")) {
                 record_t blockCounts = parseRecord(record[1]);
                 proto.write(BlockProto::ZONE, blockCounts[0]);
 
                 for (size_t i=0; i<blockHeader.size(); i++) {
-                    if (!table.insertField(proto, blockHeader[i], blockCounts[i+1])) {
+                    if (!table.insertField(&proto, blockHeader[i], blockCounts[i+1])) {
                         return BAD_VALUE;
                     }
                 }
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
index 93f970f..a4eb0fd 100644
--- a/cmds/incident_helper/src/parsers/ProcrankParser.cpp
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -46,11 +46,11 @@
             continue;
         }
 
-        if (hasPrefix(&line, "ZRAM:")) {
+        if (stripPrefix(&line, "ZRAM:")) {
             zram = line;
             continue;
         }
-        if (hasPrefix(&line, "RAM:")) {
+        if (stripPrefix(&line, "RAM:")) {
             ram = line;
             continue;
         }
@@ -68,7 +68,7 @@
 
         long long token = proto.start(Procrank::PROCESSES);
         for (int i=0; i<(int)record.size(); i++) {
-            if (!table.insertField(proto, header[i], record[i])) {
+            if (!table.insertField(&proto, header[i], record[i])) {
                 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
                         this->name.string(), nline, header[i].c_str(), record[i].c_str());
             }
@@ -82,7 +82,7 @@
         record = parseRecord(total);
         long long token = proto.start(SummaryProto::TOTAL);
         for (int i=(int)record.size(); i>0; i--) {
-            table.insertField(proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
+            table.insertField(&proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
         }
         proto.end(token);
     }
diff --git a/cmds/incident_helper/testdata/cpuinfo.txt b/cmds/incident_helper/testdata/cpuinfo.txt
new file mode 100644
index 0000000..ec4a839
--- /dev/null
+++ b/cmds/incident_helper/testdata/cpuinfo.txt
@@ -0,0 +1,15 @@
+Tasks: 2038 total,   1 running,2033 sleeping,   0 stopped,   0 zombie
+
+Mem:   3842668k total,  3761936k used,    80732k free,   220188k buffers
+
+Swap:   524284k total,    25892k used,   498392k free,  1316952k cached
+
+400%cpu  17%user   0%nice  43%sys 338%idle   0%iow   0%irq   1%sirq   0%host
+
+  PID   TID USER         PR  NI[%CPU]S VIRT  RES PCY CMD             NAME               
+
+
+29438 29438 rootabcdefghij 20 0 57.9 R  14M 3.8M     top test        top
+  916   916 system       18  -2  1.4 S 4.6G 404M  fg system_server   system_server
+   28    28 root         -2   0  1.4 S    0    0  bg rcuc/3          [rcuc/3]
+   27    27 root         RT   0  1.4 S    0    0  ta migration/3     [migration/3]
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
new file mode 100644
index 0000000..57ad15c
--- /dev/null
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CpuInfoParser.h"
+
+#include "frameworks/base/core/proto/android/os/cpuinfo.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class CpuInfoParserTest : public Test {
+public:
+    virtual void SetUp() override {
+        ASSERT_TRUE(tf.fd != -1);
+    }
+
+    string getSerializedString(::google::protobuf::Message& message) {
+        string expectedStr;
+        message.SerializeToFileDescriptor(tf.fd);
+        ReadFileToString(tf.path, &expectedStr);
+        return expectedStr;
+    }
+
+protected:
+    TemporaryFile tf;
+
+    const string kTestPath = GetExecutableDirectory();
+    const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(CpuInfoParserTest, HasSwapInfo) {
+    const string testFile = kTestDataPath + "cpuinfo.txt";
+    CpuInfoParser parser;
+    CpuInfo expected;
+
+    CpuInfo::TaskStats* taskStats = expected.mutable_task_stats();
+    taskStats->set_total(2038);
+    taskStats->set_running(1);
+    taskStats->set_sleeping(2033);
+    taskStats->set_stopped(0);
+    taskStats->set_zombie(0);
+
+    CpuInfo::MemStats* mem = expected.mutable_mem();
+    mem->set_total(3842668);
+    mem->set_used(3761936);
+    mem->set_free(80732);
+    mem->set_buffers(220188);
+
+    CpuInfo::MemStats* swap = expected.mutable_swap();
+    swap->set_total(524284);
+    swap->set_used(25892);
+    swap->set_free(498392);
+    swap->set_cached(1316952);
+
+    CpuInfo::CpuUsage* usage = expected.mutable_cpu_usage();
+    usage->set_cpu(400);
+    usage->set_user(17);
+    usage->set_nice(0);
+    usage->set_sys(43);
+    usage->set_idle(338);
+    usage->set_iow(0);
+    usage->set_irq(0);
+    usage->set_sirq(1);
+    usage->set_host(0);
+
+    // This is a special line which is able to be parsed by the CpuInfoParser
+    CpuInfo::Task* task1 = expected.add_tasks();
+    task1->set_pid(29438);
+    task1->set_tid(29438);
+    task1->set_user("rootabcdefghij");
+    task1->set_pr("20");
+    task1->set_ni(0);
+    task1->set_cpu(57.9);
+    task1->set_s(CpuInfo::Task::STATUS_R);
+    task1->set_virt("14M");
+    task1->set_res("3.8M");
+    task1->set_pcy(CpuInfo::Task::POLICY_UNKNOWN);
+    task1->set_cmd("top test");
+    task1->set_name("top");
+
+    CpuInfo::Task* task2 = expected.add_tasks();
+    task2->set_pid(916);
+    task2->set_tid(916);
+    task2->set_user("system");
+    task2->set_pr("18");
+    task2->set_ni(-2);
+    task2->set_cpu(1.4);
+    task2->set_s(CpuInfo::Task::STATUS_S);
+    task2->set_virt("4.6G");
+    task2->set_res("404M");
+    task2->set_pcy(CpuInfo::Task::POLICY_fg);
+    task2->set_cmd("system_server");
+    task2->set_name("system_server");
+
+    CpuInfo::Task* task3 = expected.add_tasks();
+    task3->set_pid(28);
+    task3->set_tid(28);
+    task3->set_user("root");
+    task3->set_pr("-2");
+    task3->set_ni(0);
+    task3->set_cpu(1.4);
+    task3->set_s(CpuInfo::Task::STATUS_S);
+    task3->set_virt("0");
+    task3->set_res("0");
+    task3->set_pcy(CpuInfo::Task::POLICY_bg);
+    task3->set_cmd("rcuc/3");
+    task3->set_name("[rcuc/3]");
+
+    CpuInfo::Task* task4 = expected.add_tasks();
+    task4->set_pid(27);
+    task4->set_tid(27);
+    task4->set_user("root");
+    task4->set_pr("RT");
+    task4->set_ni(0);
+    task4->set_cpu(1.4);
+    task4->set_s(CpuInfo::Task::STATUS_S);
+    task4->set_virt("0");
+    task4->set_res("0");
+    task4->set_pcy(CpuInfo::Task::POLICY_ta);
+    task4->set_cmd("migration/3");
+    task4->set_name("[migration/3]");
+
+    int fd = open(testFile.c_str(), O_RDONLY);
+    ASSERT_TRUE(fd != -1);
+
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+    EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+    close(fd);
+}
diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp
index da88ee3..5740b33 100644
--- a/cmds/incident_helper/tests/ih_util_test.cpp
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -62,10 +62,63 @@
     EXPECT_EQ(expected, result);
 }
 
+TEST(IhUtilTest, ParseRecordByColumns) {
+    record_t result, expected;
+    std::vector<int> indices = { 3, 10 };
+
+    result = parseRecordByColumns("12345", indices);
+    expected = {};
+    EXPECT_EQ(expected, result);
+
+    result = parseRecordByColumns("abc \t2345  6789 ", indices);
+    expected = { "abc", "2345", "6789" };
+    EXPECT_EQ(expected, result);
+
+    result = parseRecordByColumns("abc \t23456789 bob", indices);
+    expected = { "abc", "23456789", "bob" };
+    EXPECT_EQ(expected, result);
+}
+
+TEST(IhUtilTest, stripPrefix) {
+    string data1 = "Swap: abc ";
+    EXPECT_TRUE(stripPrefix(&data1, "Swap:"));
+    EXPECT_THAT(data1, StrEq("abc"));
+
+    string data2 = "Swap: abc ";
+    EXPECT_FALSE(stripPrefix(&data2, "Total:"));
+    EXPECT_THAT(data2, StrEq("Swap: abc "));
+
+    string data3 = "Swap: abc ";
+    EXPECT_TRUE(stripPrefix(&data3, "Swa"));
+    EXPECT_THAT(data3, StrEq("p: abc"));
+
+    string data4 = "Swap: abc ";
+    EXPECT_FALSE(stripPrefix(&data4, "Swa", true));
+    EXPECT_THAT(data4, StrEq("Swap: abc "));
+}
+
+TEST(IhUtilTest, stripSuffix) {
+    string data1 = " 243%abc";
+    EXPECT_TRUE(stripSuffix(&data1, "abc"));
+    EXPECT_THAT(data1, StrEq("243%"));
+
+    string data2 = " 243%abc";
+    EXPECT_FALSE(stripSuffix(&data2, "Not right"));
+    EXPECT_THAT(data2, StrEq(" 243%abc"));
+
+    string data3 = " 243%abc";
+    EXPECT_TRUE(stripSuffix(&data3, "bc"));
+    EXPECT_THAT(data3, StrEq("243%a"));
+
+    string data4 = " 243%abc";
+    EXPECT_FALSE(stripSuffix(&data4, "bc", true));
+    EXPECT_THAT(data4, StrEq(" 243%abc"));
+}
+
 TEST(IhUtilTest, Reader) {
     TemporaryFile tf;
     ASSERT_NE(tf.fd, -1);
-    ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooo\n", tf.path, false));
+    ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooo\n", tf.path));
 
     Reader r(tf.fd);
     string line;
@@ -79,40 +132,22 @@
     ASSERT_TRUE(r.ok(&line));
 }
 
-TEST(IhUtilTest, ReaderSmallBufSize) {
-    TemporaryFile tf;
-    ASSERT_NE(tf.fd, -1);
-    ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooiecccojreo", tf.path, false));
-
-    Reader r(tf.fd, 5);
-    string line;
-    ASSERT_TRUE(r.readLine(&line));
-    EXPECT_THAT(line, StrEq("test string"));
-    ASSERT_TRUE(r.readLine(&line));
-    EXPECT_THAT(line, StrEq("second"));
-    ASSERT_TRUE(r.readLine(&line));
-    EXPECT_THAT(line, StrEq("ooiecccojreo"));
-    ASSERT_FALSE(r.readLine(&line));
-    ASSERT_TRUE(r.ok(&line));
-}
-
 TEST(IhUtilTest, ReaderEmpty) {
     TemporaryFile tf;
     ASSERT_NE(tf.fd, -1);
-    ASSERT_TRUE(WriteStringToFile("", tf.path, false));
+    ASSERT_TRUE(WriteStringToFile("", tf.path));
 
     Reader r(tf.fd);
     string line;
-    ASSERT_TRUE(r.readLine(&line));
-    EXPECT_THAT(line, StrEq(""));
     ASSERT_FALSE(r.readLine(&line));
+    EXPECT_THAT(line, StrEq(""));
     ASSERT_TRUE(r.ok(&line));
 }
 
 TEST(IhUtilTest, ReaderMultipleEmptyLines) {
     TemporaryFile tf;
     ASSERT_NE(tf.fd, -1);
-    ASSERT_TRUE(WriteStringToFile("\n\n", tf.path, false));
+    ASSERT_TRUE(WriteStringToFile("\n\n", tf.path));
 
     Reader r(tf.fd);
     string line;
@@ -130,15 +165,7 @@
     string line;
     EXPECT_FALSE(r.readLine(&line));
     EXPECT_FALSE(r.ok(&line));
-    EXPECT_THAT(line, StrEq("Negative fd"));
-}
-
-TEST(IhUtilTest, ReaderFailedZeroBufferSize) {
-    Reader r(23, 0);
-    string line;
-    EXPECT_FALSE(r.readLine(&line));
-    EXPECT_FALSE(r.ok(&line));
-    EXPECT_THAT(line, StrEq("Zero buffer capacity"));
+    EXPECT_THAT(line, StrEq("Invalid fd -123"));
 }
 
 TEST(IhUtilTest, ReaderFailedBadFd) {
@@ -146,5 +173,5 @@
     string line;
     EXPECT_FALSE(r.readLine(&line));
     EXPECT_FALSE(r.ok(&line));
-    EXPECT_THAT(line, StrEq("Fail to read from fd"));
+    EXPECT_THAT(line, StrEq("Invalid fd 1231432"));
 }
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index 2afa778..3fd2ed8 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -84,7 +84,7 @@
 
 TEST_F(FdBufferTest, ReadAndWrite) {
     std::string testdata = "FdBuffer test string";
-    ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+    ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
     ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
     AssertBufferReadSuccessful(testdata.size());
     AssertBufferContent(testdata.c_str());
@@ -97,7 +97,7 @@
 
 TEST_F(FdBufferTest, ReadAndIterate) {
     std::string testdata = "FdBuffer test string";
-    ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+    ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
     ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
 
     int i=0;
@@ -137,7 +137,7 @@
 TEST_F(FdBufferTest, ReadInStreamAndWrite) {
     std::string testdata = "simply test read in stream";
     std::string expected = HEAD + testdata;
-    ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+    ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
 
     int pid = fork();
     ASSERT_TRUE(pid != -1);
@@ -166,7 +166,7 @@
 TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) {
     std::string testdata = "child process flushes only after all data are read.";
     std::string expected = HEAD + testdata;
-    ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+    ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
 
     int pid = fork();
     ASSERT_TRUE(pid != -1);
@@ -196,7 +196,7 @@
 }
 
 TEST_F(FdBufferTest, ReadInStreamEmpty) {
-    ASSERT_TRUE(WriteStringToFile("", tf.path, false));
+    ASSERT_TRUE(WriteStringToFile("", tf.path));
 
     int pid = fork();
     ASSERT_TRUE(pid != -1);
@@ -260,7 +260,7 @@
 
 TEST_F(FdBufferTest, ReadInStreamTimeOut) {
     std::string testdata = "timeout test";
-    ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+    ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
 
     int pid = fork();
     ASSERT_TRUE(pid != -1);
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 84a2a82..ca94623 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -60,7 +60,7 @@
     }
 
     void writeToFdBuffer(string str) {
-        ASSERT_TRUE(WriteStringToFile(str, tf.path, false));
+        ASSERT_TRUE(WriteStringToFile(str, tf.path));
         ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
         ASSERT_EQ(str.size(), buffer.size());
     }
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 25b05b2..649e908 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -99,7 +99,7 @@
     ReportRequestSet requests;
 
     ASSERT_TRUE(tf.fd != -1);
-    ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path, false));
+    ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path));
 
     requests.setMainFd(STDOUT_FILENO);
 
@@ -173,7 +173,7 @@
     ReportRequestSet requests;
 
     ASSERT_TRUE(tf.fd != -1);
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path, false));
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
 
     requests.setMainFd(STDOUT_FILENO);
 
@@ -186,7 +186,7 @@
     TemporaryFile input;
     FileSection fs(NOOP_PARSER, input.path);
     ReportRequestSet requests;
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
 
     IncidentReportArgs args;
     args.setAll(true);
@@ -205,7 +205,7 @@
     TemporaryFile input;
     FileSection fs(NOOP_PARSER, input.path);
     ReportRequestSet requests;
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
 
     IncidentReportArgs args;
     args.setAll(true);
@@ -223,7 +223,7 @@
     ASSERT_TRUE(output1.fd != -1);
     ASSERT_TRUE(output2.fd != -1);
     ASSERT_TRUE(output3.fd != -1);
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
 
     IncidentReportArgs args1, args2, args3;
     args1.setAll(true);
@@ -265,7 +265,7 @@
     ASSERT_TRUE(output2.fd != -1);
     ASSERT_TRUE(output3.fd != -1);
 
-    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+    ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
 
     IncidentReportArgs args1, args2, args3, args4;
     args1.setAll(true);
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 87d318b..0f6d868 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -31,14 +31,16 @@
     src/config/ConfigManager.cpp \
     src/external/StatsCompanionServicePuller.cpp \
     src/external/ResourcePowerManagerPuller.cpp \
-    src/external/StatsPullerManager.cpp \
+    src/external/CpuTimePerUidPuller.cpp \
+    src/external/CpuTimePerUidFreqPuller.cpp \
+    src/external/StatsPullerManagerImpl.cpp \
     src/logd/LogEvent.cpp \
     src/logd/LogListener.cpp \
     src/logd/LogReader.cpp \
     src/matchers/CombinationLogMatchingTracker.cpp \
     src/matchers/matcher_util.cpp \
     src/matchers/SimpleLogMatchingTracker.cpp \
-    src/metrics/CountAnomalyTracker.cpp \
+    src/anomaly/DiscreteAnomalyTracker.cpp \
     src/metrics/MetricProducer.cpp \
     src/metrics/EventMetricProducer.cpp \
     src/metrics/CountMetricProducer.cpp \
@@ -150,18 +152,20 @@
 LOCAL_SRC_FILES := \
     $(statsd_common_src) \
     tests/AnomalyMonitor_test.cpp \
-    tests/ConditionTracker_test.cpp \
+    tests/anomaly/AnomalyTracker_test.cpp \
     tests/ConfigManager_test.cpp \
     tests/indexed_priority_queue_test.cpp \
     tests/LogEntryMatcher_test.cpp \
     tests/LogReader_test.cpp \
     tests/MetricsManager_test.cpp \
     tests/UidMap_test.cpp \
+    tests/condition/CombinationConditionTracker_test.cpp \
+    tests/condition/SimpleConditionTracker_test.cpp \
     tests/metrics/OringDurationTracker_test.cpp \
     tests/metrics/MaxDurationTracker_test.cpp \
     tests/metrics/CountMetricProducer_test.cpp \
-    tests/metrics/EventMetricProducer_test.cpp
-
+    tests/metrics/EventMetricProducer_test.cpp \
+    tests/metrics/ValueMetricProducer_test.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     libgmock
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 8910523..8c70bb5 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -15,9 +15,9 @@
  */
 
 #include "Log.h"
+#include "statslog.h"
 
 #include "StatsLogProcessor.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "metrics/CountMetricProducer.h"
 #include "stats_util.h"
 
@@ -25,6 +25,14 @@
 #include <utils/Errors.h>
 
 using namespace android;
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_BOOL;
+using android::util::FIELD_TYPE_FLOAT;
+using android::util::FIELD_TYPE_INT32;
+using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
+using android::util::ProtoOutputStream;
 using std::make_unique;
 using std::unique_ptr;
 using std::vector;
@@ -33,6 +41,14 @@
 namespace os {
 namespace statsd {
 
+// for ConfigMetricsReport
+const int FIELD_ID_CONFIG_KEY = 1;
+const int FIELD_ID_METRICS = 2;
+const int FIELD_ID_UID_MAP = 3;
+// for ConfigKey
+const int FIELD_ID_UID = 1;
+const int FIELD_ID_NAME = 2;
+
 StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
                                      const std::function<void(const vector<uint8_t>&)>& pushLog)
     : mUidMap(uidMap), mPushLog(pushLog) {
@@ -48,6 +64,22 @@
         pair.second->onLogEvent(msg);
         flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
     }
+
+    // Hard-coded logic to update the isolated uid's in the uid-map.
+    // The field numbers need to be currently updated by hand with stats_events.proto
+    if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) {
+        status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
+        bool is_create = msg.GetBool(3, &err);
+        auto parent_uid = int(msg.GetLong(1, &err2));
+        auto isolated_uid = int(msg.GetLong(2, &err3));
+        if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
+            if (is_create) {
+                mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
+            } else {
+                mUidMap->removeIsolatedUid(isolated_uid, parent_uid);
+            }
+        }
+    }
 }
 
 void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
@@ -70,27 +102,46 @@
     }
 }
 
-ConfigMetricsReport StatsLogProcessor::onDumpReport(const ConfigKey& key) {
-    ConfigMetricsReport report;
-
+vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
-        return report;
+        return vector<uint8_t>();
     }
 
-    auto set_key = report.mutable_config_key();
-    set_key->set_uid(key.GetUid());
-    set_key->set_name(key.GetName());
-    for (auto m : it->second->onDumpReport()) {
-        // Transfer the vector of StatsLogReport into a field
-        // TODO: perhaps we just have bytes being returned from onDumpReport and transfer bytes
-        auto dest = report.add_metrics();
-        *dest = m;
+    ProtoOutputStream proto;
+
+    // Fill in ConfigKey.
+    long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
+    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
+    proto.write(FIELD_TYPE_STRING | FIELD_ID_NAME, key.GetName());
+    proto.end(configKeyToken);
+
+    // Fill in StatsLogReport's.
+    for (auto& m : it->second->onDumpReport()) {
+        // Add each vector of StatsLogReport into a repeated field.
+        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS,
+                    reinterpret_cast<char*>(m.get()->data()), m.get()->size());
     }
-    auto temp = mUidMap->getOutput(key);
-    report.mutable_uid_map()->Swap(&temp);
-    return report;
+
+    // Fill in UidMap.
+    auto uidMap = mUidMap->getOutput(key);
+    const int uidMapSize = uidMap.ByteSize();
+    char uidMapBuffer[uidMapSize];
+    uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
+    proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
+
+    vector<uint8_t> buffer(proto.size());
+    size_t pos = 0;
+    auto iter = proto.data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&buffer[pos], iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+
+    return buffer;
 }
 
 void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 0083827..f38d715 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -41,8 +41,7 @@
     void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
     void OnConfigRemoved(const ConfigKey& key);
 
-    // TODO: Once we have the ProtoOutputStream in c++, we can just return byte array.
-    ConfigMetricsReport onDumpReport(const ConfigKey& key);
+    vector<uint8_t> onDumpReport(const ConfigKey& key);
 
     /* Request a flush through a binder call. */
     void flush();
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 1d7e5a61..fa92f65 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -157,7 +157,7 @@
     /**
      * Fetches external metrics.
      */
-    StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+    StatsPullerManager mStatsPullerManager;
 
     /**
      * Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h
index e2ac623..d9207e9 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h
@@ -55,6 +55,7 @@
     };
 };
 
+// TODO: Rename this file to AnomalyAlarmMonitor.
 /**
  * Manages alarms for Anomaly Detection.
  */
@@ -95,6 +96,15 @@
     unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> popSoonerThan(
             uint32_t timestampSec);
 
+    // TODO: Function that uses popSoonerThan to get all alarms that have fired, and then
+    // iterates over all DurationAnomalyTracker, looking for those alarms. When they're found,
+    // have them declareAnomaly on those alarms. This means that DurationAnomalyTracker
+    // must be thread-safe (since this is being called on a different thread). There is no
+    // worry about missing the alarms (due to them being cancelled after this function being called)
+    // because DurationAnomalyTracker guarantees that it checks for anaomlies when it cancels
+    // alarms anyway.
+    // void declareAnomalies(uint32_t timestampSec);
+
     /**
      * Returns the projected alarm timestamp that is registered with
      * StatsCompanionService. This may not be equal to the soonest alarm,
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
new file mode 100644
index 0000000..6492177
--- /dev/null
+++ b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include "DiscreteAnomalyTracker.h"
+
+#include <time.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+DiscreteAnomalyTracker::DiscreteAnomalyTracker(const Alert& alert) : mAlert(alert) {
+    VLOG("DiscreteAnomalyTracker() called");
+    if (mAlert.number_of_buckets() <= 0) {
+        ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
+              (long long)mAlert.number_of_buckets());
+        return;
+    }
+    mPastBuckets.resize(mAlert.number_of_buckets());
+    reset(); // initialization
+}
+
+DiscreteAnomalyTracker::~DiscreteAnomalyTracker() {
+    VLOG("~DiscreteAnomalyTracker() called");
+}
+
+void DiscreteAnomalyTracker::reset() {
+    VLOG("reset() called.");
+    mPastBuckets.clear();
+    mPastBuckets.resize(mAlert.number_of_buckets());
+    mSumOverPastBuckets.clear();
+    mCurrentBucketIndex = -1;
+    mLastAlarmAtBucketIndex = -1;
+    mAnomalyDeclared = 0;
+}
+
+size_t DiscreteAnomalyTracker::index(int64_t bucketNum) {
+    return bucketNum % mAlert.number_of_buckets();
+}
+
+void DiscreteAnomalyTracker::addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues,
+                                               int64_t bucketIndex) {
+    VLOG("addPastBucket() called.");
+    if (bucketIndex <= mCurrentBucketIndex - mAlert.number_of_buckets()) {
+        ALOGE("Cannot add a past bucket %lld units in past", (long long)bucketIndex);
+        return;
+    }
+
+    // Empty out old mPastBuckets[i] values and update mSumOverPastBuckets.
+    if (bucketIndex - mCurrentBucketIndex >= mAlert.number_of_buckets()) {
+        mPastBuckets.clear();
+        mPastBuckets.resize(mAlert.number_of_buckets());
+        mSumOverPastBuckets.clear();
+    } else {
+        for (int64_t i = std::max(
+                     0LL, (long long)(mCurrentBucketIndex - mAlert.number_of_buckets() + 1));
+             i < bucketIndex - mAlert.number_of_buckets(); i++) {
+            const int idx = index(i);
+            subtractBucketFromSum(mPastBuckets[idx]);
+            mPastBuckets[idx] = nullptr;  // release (but not clear) the old bucket.
+        }
+    }
+    subtractBucketFromSum(mPastBuckets[index(bucketIndex)]);
+    mPastBuckets[index(bucketIndex)] = nullptr;  // release (but not clear) the old bucket.
+
+    // Replace the oldest bucket with the new bucket we are adding.
+    mPastBuckets[index(bucketIndex)] = BucketValues;
+    addBucketToSum(BucketValues);
+
+    mCurrentBucketIndex = std::max(mCurrentBucketIndex, bucketIndex);
+}
+
+void DiscreteAnomalyTracker::subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket) {
+    if (bucket == nullptr) {
+        return;
+    }
+    // For each dimension present in the bucket, subtract its value from its corresponding sum.
+    for (const auto& keyValuePair : *bucket) {
+        auto itr = mSumOverPastBuckets.find(keyValuePair.first);
+        if (itr == mSumOverPastBuckets.end()) {
+            continue;
+        }
+        itr->second -= keyValuePair.second;
+        // TODO: No need to look up the object twice like this. Use a var.
+        if (itr->second == 0) {
+            mSumOverPastBuckets.erase(itr);
+        }
+    }
+}
+
+void DiscreteAnomalyTracker::addBucketToSum(const shared_ptr<const DimToValMap>& bucket) {
+    if (bucket == nullptr) {
+        return;
+    }
+    // For each dimension present in the bucket, add its value to its corresponding sum.
+    for (const auto& keyValuePair : *bucket) {
+        mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
+    }
+}
+
+bool DiscreteAnomalyTracker::detectAnomaly() {
+    for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
+        if (mAlert.has_trigger_if_sum_gt() && itr->second > mAlert.trigger_if_sum_gt()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void DiscreteAnomalyTracker::declareAndDeclareAnomaly() {
+    if (detectAnomaly()) {
+        declareAnomaly();
+    }
+}
+
+void DiscreteAnomalyTracker::declareAnomaly() {
+    if (mLastAlarmAtBucketIndex >= 0 && mCurrentBucketIndex - mLastAlarmAtBucketIndex <=
+                                        (long long)mAlert.refractory_period_in_buckets()) {
+        VLOG("Skipping anomaly check since within refractory period");
+        return;
+    }
+    mAnomalyDeclared++;
+    // TODO(guardrail): Consider guarding against too short refractory periods.
+    mLastAlarmAtBucketIndex = mCurrentBucketIndex;
+
+    if (mAlert.has_incidentd_details()) {
+        // TODO: Can construct a name based on the criteria (and/or relay the criteria).
+        ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
+        // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
+    } else {
+        ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
new file mode 100644
index 0000000..ed7d5d7
--- /dev/null
+++ b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+#include "stats_util.h" // HashableDimensionKey and DimToValMap
+
+#include <memory> // unique_ptr
+#include <stdlib.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::unordered_map;
+using std::shared_ptr;
+
+// This anomaly track assmues that all values are non-negative.
+class DiscreteAnomalyTracker {
+ public:
+    DiscreteAnomalyTracker(const Alert& alert);
+
+    virtual ~DiscreteAnomalyTracker();
+
+    // Adds a new bucket or updates an existing bucket.
+    // Bucket index starts from 0.
+    void addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues, int64_t bucketIndex);
+
+    // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
+    bool detectAnomaly();
+
+    // Informs incidentd about the detected alert.
+    void declareAnomaly();
+
+    // Detects the alert and informs the incidentd when applicable.
+    void declareAndDeclareAnomaly();
+
+private:
+    // statsd_config.proto Alert message that defines this tracker.
+    const Alert mAlert;
+
+    // The exisiting bucket list.
+    std::vector<shared_ptr<const DimToValMap>> mPastBuckets;
+
+    // Sum over all existing buckets cached in mPastBuckets.
+    DimToValMap mSumOverPastBuckets;
+
+    // Current bucket index of the current anomaly detection window. Bucket index starts from 0.
+    int64_t mCurrentBucketIndex = -1;
+
+    // The bucket index when the last anomaly was declared.
+    int64_t mLastAlarmAtBucketIndex = -1;
+
+    // The total number of declared anomalies.
+    int64_t mAnomalyDeclared = 0;
+
+    // Add the information in the given bucket to mSumOverPastBuckets.
+    void addBucketToSum(const shared_ptr<const DimToValMap>& bucket);
+
+    // Subtract the information in the given bucket from mSumOverPastBuckets
+    // and remove any items with value 0.
+    void subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket);
+
+    // Calculates the corresponding bucket index within the circular array.
+    size_t index(int64_t bucketNum);
+
+    // Resets all data. For use when all the data gets stale.
+    void reset();
+
+    FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
+    FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 953bcb3..41f5fca 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -115,49 +115,47 @@
             evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
 }
 
-bool CombinationConditionTracker::evaluateCondition(
+void CombinationConditionTracker::evaluateCondition(
         const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
         const std::vector<sp<ConditionTracker>>& mAllConditions,
         std::vector<ConditionState>& nonSlicedConditionCache,
-        std::vector<bool>& nonSlicedChangedCache, vector<bool>& slicedConditionChanged) {
+        std::vector<bool>& conditionChangedCache) {
     // value is up to date.
     if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
-        return false;
+        return;
     }
 
     for (const int childIndex : mChildren) {
         if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
             const sp<ConditionTracker>& child = mAllConditions[childIndex];
             child->evaluateCondition(event, eventMatcherValues, mAllConditions,
-                                     nonSlicedConditionCache, nonSlicedChangedCache,
-                                     slicedConditionChanged);
+                                     nonSlicedConditionCache, conditionChangedCache);
         }
     }
 
-    ConditionState newCondition =
-            evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
+    if (!mSliced) {
+        ConditionState newCondition =
+                evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
 
-    bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
-    mNonSlicedConditionState = newCondition;
+        bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
+        mNonSlicedConditionState = newCondition;
 
-    nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
+        nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
 
-    nonSlicedChangedCache[mIndex] = nonSlicedChanged;
-
-    if (mSliced) {
+        conditionChangedCache[mIndex] = nonSlicedChanged;
+    } else {
         for (const int childIndex : mChildren) {
             // If any of the sliced condition in children condition changes, the combination
             // condition may be changed too.
-            if (slicedConditionChanged[childIndex]) {
-                slicedConditionChanged[mIndex] = true;
+            if (conditionChangedCache[childIndex]) {
+                conditionChangedCache[mIndex] = true;
                 break;
             }
         }
+        nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
         ALOGD("CombinationCondition %s sliced may changed? %d", mName.c_str(),
-              slicedConditionChanged[mIndex] == true);
+              conditionChangedCache[mIndex] == true);
     }
-
-    return nonSlicedChanged;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index dbdb3b7..3d2c6bb 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -35,12 +35,11 @@
               const std::unordered_map<std::string, int>& conditionNameIndexMap,
               std::vector<bool>& stack) override;
 
-    bool evaluateCondition(const LogEvent& event,
+    void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
                            const std::vector<sp<ConditionTracker>>& mAllConditions,
                            std::vector<ConditionState>& conditionCache,
-                           std::vector<bool>& changedCache,
-                           std::vector<bool>& slicedConditionMayChanged) override;
+                           std::vector<bool>& changedCache) override;
 
     void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index bb5ddeb..0ac7ef3 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -56,25 +56,20 @@
                       std::vector<bool>& stack) = 0;
 
     // evaluate current condition given the new event.
-    // return true if the condition state changed, false if the condition state is not changed.
     // event: the new log event
     // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
     //                     event before ConditionTrackers, because ConditionTracker depends on
     //                     LogMatchingTrackers.
     // mAllConditions: the list of all ConditionTracker
     // conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event.
-    // nonSlicedConditionChanged: the bit map to record whether non-sliced condition has changed.
-    // slicedConditionMayChanged: the bit map to record whether sliced condition may have changed.
-    //      Because sliced condition needs parameters to determine the value. So the sliced
-    //      condition is not pushed to metrics. We only inform the relevant metrics that the sliced
-    //      condition may have changed, and metrics should pull the conditions that they are
-    //      interested in.
-    virtual bool evaluateCondition(const LogEvent& event,
+    // conditionChanged: the bit map to record whether the condition has changed.
+    //                   If the condition has dimension, then any sub condition changes will report
+    //                   conditionChanged.
+    virtual void evaluateCondition(const LogEvent& event,
                                    const std::vector<MatchingState>& eventMatcherValues,
                                    const std::vector<sp<ConditionTracker>>& mAllConditions,
                                    std::vector<ConditionState>& conditionCache,
-                                   std::vector<bool>& nonSlicedConditionChanged,
-                                   std::vector<bool>& slicedConditionMayChanged) = 0;
+                                   std::vector<bool>& conditionChanged) = 0;
 
     // Return the current condition state.
     virtual ConditionState isConditionMet() {
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index b691faea..a694dbf 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -74,13 +74,21 @@
         mStopAllLogMatcherIndex = -1;
     }
 
-    mDimension.insert(mDimension.begin(), simpleCondition.dimension().begin(),
-                      simpleCondition.dimension().end());
+    mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(),
+                            simpleCondition.dimension().end());
 
-    if (mDimension.size() > 0) {
+    if (mOutputDimension.size() > 0) {
         mSliced = true;
     }
 
+    if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) {
+        mInitialValue = ConditionState::kFalse;
+    } else {
+        mInitialValue = ConditionState::kUnknown;
+    }
+
+    mNonSlicedConditionState = mInitialValue;
+
     mInitialized = true;
 }
 
@@ -97,127 +105,166 @@
     return mInitialized;
 }
 
-void print(unordered_map<HashableDimensionKey, ConditionState>& conditions, const string& name) {
+void print(map<HashableDimensionKey, int>& conditions, const string& name) {
     VLOG("%s DUMP:", name.c_str());
-
     for (const auto& pair : conditions) {
-        VLOG("\t%s %d", pair.first.c_str(), pair.second);
+        VLOG("\t%s : %d", pair.first.c_str(), pair.second);
     }
 }
 
-bool SimpleConditionTracker::evaluateCondition(const LogEvent& event,
+void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
+                                           std::vector<bool>& conditionChangedCache) {
+    // Unless the default condition is false, and there was nothing started, otherwise we have
+    // triggered a condition change.
+    conditionChangedCache[mIndex] =
+            (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
+                                                                                           : true;
+
+    // After StopAll, we know everything has stopped. From now on, default condition is false.
+    mInitialValue = ConditionState::kFalse;
+    mSlicedConditionState.clear();
+    conditionCache[mIndex] = ConditionState::kFalse;
+}
+
+void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
+                                                  bool matchStart,
+                                                  std::vector<ConditionState>& conditionCache,
+                                                  std::vector<bool>& conditionChangedCache) {
+    bool changed = false;
+    auto outputIt = mSlicedConditionState.find(outputKey);
+    ConditionState newCondition;
+    if (outputIt == mSlicedConditionState.end()) {
+        // We get a new output key.
+        newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
+        if (matchStart && mInitialValue != ConditionState::kTrue) {
+            mSlicedConditionState[outputKey] = 1;
+            changed = true;
+        } else if (mInitialValue != ConditionState::kFalse) {
+            // it's a stop and we don't have history about it.
+            // If the default condition is not false, it means this stop is valuable to us.
+            mSlicedConditionState[outputKey] = 0;
+            changed = true;
+        }
+    } else {
+        // we have history about this output key.
+        auto& startedCount = outputIt->second;
+        // assign the old value first.
+        newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+        if (matchStart) {
+            if (startedCount == 0) {
+                // This condition for this output key will change from false -> true
+                changed = true;
+            }
+
+            // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
+            // as 1 if not counting nesting.
+            startedCount++;
+            newCondition = ConditionState::kTrue;
+        } else {
+            // This is a stop event.
+            if (startedCount > 0) {
+                if (mCountNesting) {
+                    startedCount--;
+                    if (startedCount == 0) {
+                        newCondition = ConditionState::kFalse;
+                    }
+                } else {
+                    // not counting nesting, so ignore the number of starts, stop now.
+                    startedCount = 0;
+                    newCondition = ConditionState::kFalse;
+                }
+                // if everything has stopped for this output key, condition true -> false;
+                if (startedCount == 0) {
+                    changed = true;
+                }
+            }
+
+            // if default condition is false, it means we don't need to keep the false values.
+            if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
+                mSlicedConditionState.erase(outputIt);
+                VLOG("erase key %s", outputKey.c_str());
+            }
+        }
+    }
+
+    // dump all dimensions for debugging
+    if (DEBUG) {
+        print(mSlicedConditionState, mName);
+    }
+
+    conditionChangedCache[mIndex] = changed;
+    conditionCache[mIndex] = newCondition;
+
+    VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(),
+         conditionChangedCache[mIndex] == true);
+}
+
+void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
                                                const vector<MatchingState>& eventMatcherValues,
                                                const vector<sp<ConditionTracker>>& mAllConditions,
                                                vector<ConditionState>& conditionCache,
-                                               vector<bool>& nonSlicedConditionChanged,
-                                               std::vector<bool>& slicedConditionChanged) {
+                                               vector<bool>& conditionChangedCache) {
     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
         // it has been evaluated.
         VLOG("Yes, already evaluated, %s %d", mName.c_str(), mNonSlicedConditionState);
-        return false;
+        return;
     }
 
-    // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions.
+    if (mStopAllLogMatcherIndex >= 0 &&
+        eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
+        handleStopAll(conditionCache, conditionChangedCache);
+        return;
+    }
 
-    ConditionState newCondition = mNonSlicedConditionState;
-    bool matched = false;
+    int matchedState = -1;
     // Note: The order to evaluate the following start, stop, stop_all matters.
     // The priority of overwrite is stop_all > stop > start.
     if (mStartLogMatcherIndex >= 0 &&
         eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
-        matched = true;
-        newCondition = ConditionState::kTrue;
+        matchedState = 1;
     }
 
     if (mStopLogMatcherIndex >= 0 &&
         eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
-        matched = true;
-        newCondition = ConditionState::kFalse;
+        matchedState = 0;
     }
 
-    bool stopAll = false;
-    if (mStopAllLogMatcherIndex >= 0 &&
-        eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
-        matched = true;
-        newCondition = ConditionState::kFalse;
-        stopAll = true;
-    }
-
-    if (matched == false) {
-        slicedConditionChanged[mIndex] = false;
-        nonSlicedConditionChanged[mIndex] = false;
+    if (matchedState < 0) {
+        conditionChangedCache[mIndex] = false;
         conditionCache[mIndex] = mNonSlicedConditionState;
-        return false;
+        return;
     }
 
-    bool nonSlicedChanged = mNonSlicedConditionState != newCondition;
-
-    bool slicedChanged = false;
-
-    if (stopAll) {
-        // TODO: handle stop all; all dimension should be cleared.
-    }
-
-
-    if (mDimension.size() > 0) {
-        HashableDimensionKey hashableKey = getHashableKey(getDimensionKey(event, mDimension));
-        if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
-            mSlicedConditionState[hashableKey] != newCondition) {
-            slicedChanged = true;
-            mSlicedConditionState[hashableKey] = newCondition;
-        }
-        VLOG("key: %s %d", hashableKey.c_str(), newCondition);
-        // dump all dimensions for debugging
-        if (DEBUG) {
-            print(mSlicedConditionState, mName);
-        }
-    }
-
-    // even if this SimpleCondition is not sliced, it may be part of a sliced CombinationCondition
-    // if the nonSliced condition changed, it may affect the sliced condition in the parent node.
-    // so mark the slicedConditionChanged to be true.
-    // For example: APP_IN_BACKGROUND_OR_SCREEN_OFF
-    //     APP_IN_BACKGROUND is sliced [App_A->True, App_B->False].
-    //     SCREEN_OFF is not sliced, and it changes from False -> True;
-    //     We need to populate this change to parent condition. Because for App_B,
-    //     the APP_IN_BACKGROUND_OR_SCREEN_OFF condition would change from False->True.
-    slicedConditionChanged[mIndex] = mSliced ? slicedChanged : nonSlicedChanged;
-    nonSlicedConditionChanged[mIndex] = nonSlicedChanged;
-
-    VLOG("SimpleCondition %s nonSlicedChange? %d  SlicedChanged? %d", mName.c_str(),
-         nonSlicedConditionChanged[mIndex] == true, slicedConditionChanged[mIndex] == true);
-    mNonSlicedConditionState = newCondition;
-    conditionCache[mIndex] = mNonSlicedConditionState;
-
-    return nonSlicedConditionChanged[mIndex];
+    // outputKey is the output key values. e.g, uid:1234
+    const HashableDimensionKey outputKey = getHashableKey(getDimensionKey(event, mOutputDimension));
+    handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache);
 }
 
 void SimpleConditionTracker::isConditionMet(
         const map<string, HashableDimensionKey>& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions, vector<ConditionState>& conditionCache) {
     const auto pair = conditionParameters.find(mName);
-    if (pair == conditionParameters.end()) {
-        // the query does not need my sliced condition. just return the non sliced condition.
-        conditionCache[mIndex] = mNonSlicedConditionState;
-        VLOG("Condition %s return %d", mName.c_str(), mNonSlicedConditionState);
+    HashableDimensionKey key =
+            (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
+
+    if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
+        ALOGE("Condition %s output has dimension, but it's not specified in the query!",
+              mName.c_str());
+        conditionCache[mIndex] = mInitialValue;
         return;
     }
 
-    const HashableDimensionKey& key = pair->second;
     VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str());
 
-    if (mSlicedConditionState.find(key) == mSlicedConditionState.end()) {
-        // never seen this key before. the condition is unknown to us.
-        conditionCache[mIndex] = ConditionState::kUnknown;
+    auto startedCountIt = mSlicedConditionState.find(key);
+    if (startedCountIt == mSlicedConditionState.end()) {
+        conditionCache[mIndex] = mInitialValue;
     } else {
-        conditionCache[mIndex] = mSlicedConditionState[key];
+        conditionCache[mIndex] =
+                startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
     }
 
     VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]);
-
-    if (DEBUG) {
-        print(mSlicedConditionState, mName);
-    }
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index b72157b..2eda0b1 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -17,6 +17,7 @@
 #ifndef SIMPLE_CONDITION_TRACKER_H
 #define SIMPLE_CONDITION_TRACKER_H
 
+#include <gtest/gtest_prod.h>
 #include "ConditionTracker.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
@@ -38,12 +39,11 @@
               const std::unordered_map<std::string, int>& conditionNameIndexMap,
               std::vector<bool>& stack) override;
 
-    bool evaluateCondition(const LogEvent& event,
+    void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
                            const std::vector<sp<ConditionTracker>>& mAllConditions,
                            std::vector<ConditionState>& conditionCache,
-                           std::vector<bool>& changedCache,
-                           std::vector<bool>& slicedChangedCache) override;
+                           std::vector<bool>& changedCache) override;
 
     void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
@@ -62,15 +62,22 @@
     // The index of the LogEventMatcher which defines the stop all.
     int mStopAllLogMatcherIndex;
 
-    // The dimension defines at the atom level, how start and stop should match.
-    // e.g., APP_IN_FOREGROUND, the dimension should be the uid field. Each "start" and
-    // "stop" tells you the state change of a particular app. Without this dimension, this
-    // condition does not make sense.
-    std::vector<KeyMatcher> mDimension;
+    ConditionState mInitialValue;
 
-    // Keep the map from the internal HashableDimensionKey to std::vector<KeyValuePair>
-    // that StatsLogReport wants.
-    std::unordered_map<HashableDimensionKey, ConditionState> mSlicedConditionState;
+    std::vector<KeyMatcher> mOutputDimension;
+
+    std::map<HashableDimensionKey, int> mSlicedConditionState;
+
+    void handleStopAll(std::vector<ConditionState>& conditionCache,
+                       std::vector<bool>& changedCache);
+
+    void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+                              std::vector<ConditionState>& conditionCache,
+                              std::vector<bool>& changedCache);
+
+    FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
+    FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
+    FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 40d41be..2618a21 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -25,7 +25,6 @@
 #include <unordered_map>
 #include "../matchers/matcher_util.h"
 #include "ConditionTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
 
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index 47e245e..4167bf9 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -19,7 +19,6 @@
 
 #include <vector>
 #include "../matchers/matcher_util.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
 namespace android {
diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h
index a58766d..19ccfcf 100644
--- a/cmds/statsd/src/config/ConfigListener.h
+++ b/cmds/statsd/src/config/ConfigListener.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
 #include "config/ConfigKey.h"
 
 #include <utils/RefBase.h>
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 02e6903..1e1d88e 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -116,7 +116,7 @@
 static StatsdConfig build_fake_config() {
     // HACK: Hard code a test metric for counting screen on events...
     StatsdConfig config;
-    config.set_config_id(12345L);
+    config.set_name("12345");
 
     int WAKE_LOCK_TAG_ID = 1111;  // put a fake id here to make testing easier.
     int WAKE_LOCK_UID_KEY_ID = 1;
@@ -147,27 +147,35 @@
 
     // Count Screen ON events.
     CountMetric* metric = config.add_count_metric();
-    metric->set_metric_id(1);
+    metric->set_name("1");
     metric->set_what("SCREEN_TURNED_ON");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
 
     // Anomaly threshold for screen-on count.
-    Alert* alert = metric->add_alerts();
+    Alert* alert = config.add_alerts();
+    alert->set_name("1");
     alert->set_number_of_buckets(6);
     alert->set_trigger_if_sum_gt(10);
     alert->set_refractory_period_secs(30);
 
     // Count process state changes, slice by uid.
     metric = config.add_count_metric();
-    metric->set_metric_id(2);
+    metric->set_name("2");
     metric->set_what("PROCESS_STATE_CHANGE");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     KeyMatcher* keyMatcher = metric->add_dimension();
     keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
 
+    // Anomaly threshold for background count.
+    alert = config.add_alerts();
+    alert->set_name("2");
+    alert->set_number_of_buckets(4);
+    alert->set_trigger_if_sum_gt(30);
+    alert->set_refractory_period_secs(20);
+
     // Count process state changes, slice by uid, while SCREEN_IS_OFF
     metric = config.add_count_metric();
-    metric->set_metric_id(3);
+    metric->set_name("3");
     metric->set_what("PROCESS_STATE_CHANGE");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     keyMatcher = metric->add_dimension();
@@ -176,7 +184,7 @@
 
     // Count wake lock, slice by uid, while SCREEN_IS_ON and app in background
     metric = config.add_count_metric();
-    metric->set_metric_id(4);
+    metric->set_name("4");
     metric->set_what("APP_GET_WL");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     keyMatcher = metric->add_dimension();
@@ -189,12 +197,12 @@
 
     // Duration of an app holding any wl, while screen on and app in background, slice by uid
     DurationMetric* durationMetric = config.add_duration_metric();
-    durationMetric->set_metric_id(5);
+    durationMetric->set_name("5");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
     keyMatcher = durationMetric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
-    durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
@@ -203,12 +211,12 @@
 
     // max Duration of an app holding any wl, while screen on and app in background, slice by uid
     durationMetric = config.add_duration_metric();
-    durationMetric->set_metric_id(6);
+    durationMetric->set_name("6");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
     keyMatcher = durationMetric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
-    durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
@@ -217,10 +225,10 @@
 
     // Duration of an app holding any wl, while screen on and app in background
     durationMetric = config.add_duration_metric();
-    durationMetric->set_metric_id(7);
+    durationMetric->set_name("7");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
-    durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
@@ -229,14 +237,14 @@
 
     // Duration of screen on time.
     durationMetric = config.add_duration_metric();
-    durationMetric->set_metric_id(8);
+    durationMetric->set_name("8");
     durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L);
     durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
     durationMetric->set_what("SCREEN_IS_ON");
 
     // Value metric to count KERNEL_WAKELOCK when screen turned on
     ValueMetric* valueMetric = config.add_value_metric();
-    valueMetric->set_metric_id(6);
+    valueMetric->set_name("6");
     valueMetric->set_what("KERNEL_WAKELOCK");
     valueMetric->set_value_field(1);
     valueMetric->set_condition("SCREEN_IS_ON");
@@ -247,12 +255,12 @@
 
     // Add an EventMetric to log process state change events.
     EventMetric* eventMetric = config.add_event_metric();
-    eventMetric->set_metric_id(9);
+    eventMetric->set_name("9");
     eventMetric->set_what("SCREEN_TURNED_ON");
 
     // Add an GaugeMetric.
     GaugeMetric* gaugeMetric = config.add_gauge_metric();
-    gaugeMetric->set_metric_id(10);
+    gaugeMetric->set_name("10");
     gaugeMetric->set_what("DEVICE_TEMPERATURE");
     gaugeMetric->set_gauge_field(DEVICE_TEMPERATURE_KEY);
     gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
@@ -328,12 +336,14 @@
     SimpleCondition* simpleCondition = condition->mutable_simple_condition();
     simpleCondition->set_start("SCREEN_TURNED_ON");
     simpleCondition->set_stop("SCREEN_TURNED_OFF");
+    simpleCondition->set_count_nesting(false);
 
     condition = config.add_condition();
     condition->set_name("SCREEN_IS_OFF");
     simpleCondition = condition->mutable_simple_condition();
     simpleCondition->set_start("SCREEN_TURNED_OFF");
     simpleCondition->set_stop("SCREEN_TURNED_ON");
+    simpleCondition->set_count_nesting(false);
 
     condition = config.add_condition();
     condition->set_name("APP_IS_BACKGROUND");
@@ -342,6 +352,7 @@
     simpleCondition->set_stop("APP_GOES_FOREGROUND");
     KeyMatcher* condition_dimension1 = simpleCondition->add_dimension();
     condition_dimension1->set_key(APP_USAGE_UID_KEY_ID);
+    simpleCondition->set_count_nesting(false);
 
     condition = config.add_condition();
     condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
@@ -351,7 +362,7 @@
     combination_condition->add_condition("SCREEN_IS_ON");
 
     condition = config.add_condition();
-    condition->set_name("WL_STATE_PER_APP_PER_NAME");
+    condition->set_name("WL_HELD_PER_APP_PER_NAME");
     simpleCondition = condition->mutable_simple_condition();
     simpleCondition->set_start("APP_GET_WL");
     simpleCondition->set_stop("APP_RELEASE_WL");
@@ -359,6 +370,17 @@
     condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
     condition_dimension = simpleCondition->add_dimension();
     condition_dimension->set_key(WAKE_LOCK_NAME_KEY);
+    simpleCondition->set_count_nesting(true);
+
+    condition = config.add_condition();
+    condition->set_name("WL_HELD_PER_APP");
+    simpleCondition = condition->mutable_simple_condition();
+    simpleCondition->set_start("APP_GET_WL");
+    simpleCondition->set_stop("APP_RELEASE_WL");
+    simpleCondition->set_initial_value(SimpleCondition_InitialValue_FALSE);
+    condition_dimension = simpleCondition->add_dimension();
+    condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+    simpleCondition->set_count_nesting(true);
 
     return config;
 }
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
new file mode 100644
index 0000000..e004d21
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -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.
+ */
+
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "external/CpuTimePerUidFreqPuller.h"
+
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_time_in_state which has the format:
+ *
+ * uid: [freq1] [freq2] [freq3] ...
+ * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+ * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+ * ...
+ *
+ * This provides the times a UID's processes spent executing at each different cpu frequency.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+bool CpuTimePerUidFreqPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>* data) {
+  data->clear();
+
+  ifstream fin;
+  fin.open(sProcFile);
+  if (!fin.good()) {
+    VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+    return false;
+  }
+
+  uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+  char buf[kLineBufferSize];
+  // first line prints the format and frequencies
+  fin.getline(buf, kLineBufferSize);
+  char * pch;
+  while(!fin.eof()){
+    fin.getline(buf, kLineBufferSize);
+    pch = strtok (buf, " :");
+    if (pch == NULL) break;
+    uint64_t uid = std::stoull(pch);
+    pch = strtok(NULL, " ");
+    uint64_t timeMs;
+    int idx = 0;
+    do {
+      timeMs = std::stoull(pch);
+      auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ_PULLED, timestamp);
+      auto elemList = ptr->GetAndroidLogEventList();
+      *elemList << uid;
+      *elemList << idx;
+      *elemList << timeMs;
+      ptr->init();
+      data->push_back(ptr);
+      VLOG("uid %lld, freq idx %d, sys time %lld", (long long)uid, idx, (long long)timeMs);
+      idx ++;
+      pch = strtok(NULL, " ");
+    } while (pch != NULL);
+  }
+  return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h
new file mode 100644
index 0000000..839e5aa
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class CpuTimePerUidFreqPuller : public StatsPuller {
+ public:
+  bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
new file mode 100644
index 0000000..b84b877
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -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.
+ */
+
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "external/CpuTimePerUidPuller.h"
+
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+bool CpuTimePerUidPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>* data) {
+  data->clear();
+
+  ifstream fin;
+  fin.open(sProcFile);
+  if (!fin.good()) {
+    VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+    return false;
+  }
+
+  uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+  char buf[kLineBufferSize];
+  char * pch;
+  while(!fin.eof()){
+    fin.getline(buf, kLineBufferSize);
+    pch = strtok(buf, " :");
+    if (pch == NULL) break;
+    uint64_t uid = std::stoull(pch);
+    pch = strtok(buf, " ");
+    uint64_t userTimeMs = std::stoull(pch);
+    pch = strtok(buf, " ");
+    uint64_t sysTimeMs = std::stoull(pch);
+
+    auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_PULLED, timestamp);
+    auto elemList = ptr->GetAndroidLogEventList();
+    *elemList << uid;
+    *elemList << userTimeMs;
+    *elemList << sysTimeMs;
+    ptr->init();
+    data->push_back(ptr);
+    VLOG("uid %lld, user time %lld, sys time %lld", (long long)uid, (long long)userTimeMs, (long long)sysTimeMs);
+  }
+  return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.h b/cmds/statsd/src/external/CpuTimePerUidPuller.h
new file mode 100644
index 0000000..9bb8946
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class CpuTimePerUidPuller : public StatsPuller {
+ public:
+  bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
index 3608ee4..319feef4 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
@@ -34,6 +34,7 @@
 #include "external/StatsPuller.h"
 
 #include "logd/LogEvent.h"
+#include "statslog.h"
 
 using android::hardware::hidl_vec;
 using android::hardware::power::V1_0::IPower;
@@ -57,10 +58,6 @@
 std::mutex gPowerHalMutex;
 bool gPowerHalExists = true;
 
-static const int power_state_platform_sleep_state_tag = 1011;
-static const int power_state_voter_tag = 1012;
-static const int power_state_subsystem_state_tag = 1013;
-
 bool getPowerHal() {
     if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
         gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
@@ -94,8 +91,8 @@
                 for (size_t i = 0; i < states.size(); i++) {
                     const PowerStatePlatformSleepState& state = states[i];
 
-                    auto statePtr =
-                            make_shared<LogEvent>(power_state_platform_sleep_state_tag, timestamp);
+                    auto statePtr = make_shared<LogEvent>(
+                            android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, timestamp);
                     auto elemList = statePtr->GetAndroidLogEventList();
                     *elemList << state.name;
                     *elemList << state.residencyInMsecSinceBoot;
@@ -107,12 +104,14 @@
                          (long long)state.residencyInMsecSinceBoot,
                          (long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0);
                     for (auto voter : state.voters) {
-                        auto voterPtr = make_shared<LogEvent>(power_state_voter_tag, timestamp);
+                        auto voterPtr =
+                                make_shared<LogEvent>(android::util::POWER_STATE_VOTER_PULLED, timestamp);
                         auto elemList = voterPtr->GetAndroidLogEventList();
                         *elemList << state.name;
                         *elemList << voter.name;
                         *elemList << voter.totalTimeInMsecVotedForSinceBoot;
                         *elemList << voter.totalNumberOfTimesVotedSinceBoot;
+                        voterPtr->init();
                         data->push_back(voterPtr);
                         VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
                              voter.name.c_str(), (long long)voter.totalTimeInMsecVotedForSinceBoot,
@@ -141,7 +140,7 @@
                             for (size_t j = 0; j < subsystem.states.size(); j++) {
                                 const PowerStateSubsystemSleepState& state = subsystem.states[j];
                                 auto subsystemStatePtr = make_shared<LogEvent>(
-                                        power_state_subsystem_state_tag, timestamp);
+                                        android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, timestamp);
                                 auto elemList = subsystemStatePtr->GetAndroidLogEventList();
                                 *elemList << subsystem.name;
                                 *elemList << state.name;
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 67580d6..2e803c9 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,71 +16,39 @@
 
 #pragma once
 
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IServiceManager.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/String8.h>
-#include <utils/threads.h>
-#include <string>
-#include <unordered_map>
-#include <vector>
-#include "PullDataReceiver.h"
-#include "StatsPuller.h"
-#include "logd/LogEvent.h"
+#include "StatsPullerManagerImpl.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
-class StatsPullerManager : public virtual RefBase {
-public:
-    static StatsPullerManager& GetInstance();
+class StatsPullerManager{
+ public:
+  virtual ~StatsPullerManager() {}
 
-    void RegisterReceiver(int tagId, sp<PullDataReceiver> receiver, long intervalMs);
+  virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs) {
+    mPullerManager.RegisterReceiver(tagId, receiver, intervalMs);
+  };
 
-    void UnRegisterReceiver(int tagId, sp<PullDataReceiver> receiver);
+  virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
+    mPullerManager.UnRegisterReceiver(tagId, receiver);
+  };
 
-    // Verify if we know how to pull for this matcher
-    bool PullerForMatcherExists(int tagId);
+  // Verify if we know how to pull for this matcher
+  bool PullerForMatcherExists(int tagId) {
+    return mPullerManager.PullerForMatcherExists(tagId);
+  }
 
-    void OnAlarmFired();
+  void OnAlarmFired() {
+    mPullerManager.OnAlarmFired();
+  }
 
-    bool Pull(const int pullCode, vector<std::shared_ptr<LogEvent>>* data);
+  virtual bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+    return mPullerManager.Pull(tagId, data);
+  }
 
-private:
-    StatsPullerManager();
-
-    // use this to update alarm
-    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
-
-    sp<IStatsCompanionService> get_stats_companion_service();
-
-    // mapping from simple matcher tagId to puller
-    std::map<int, std::shared_ptr<StatsPuller>> mPullers;
-
-      typedef struct {
-        // pull_interval_sec : last_pull_time_sec
-        std::pair<uint64_t, uint64_t> timeInfo;
-        sp<PullDataReceiver> receiver;
-      } ReceiverInfo;
-
-    // mapping from simple matcher tagId to receivers
-    std::map<int, std::vector<ReceiverInfo>> mReceivers;
-
-    Mutex mReceiversLock;
-
-    long mCurrentPullingInterval;
-
-    // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
-    // bucket size. All pulled metrics start pulling based on this time, so that they can be
-    // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
-    // request time.
-    const long mPullStartTimeMs;
-
-    long get_pull_start_time_ms();
-
-    LogEvent parse_pulled_data(String16 data);
+ private:
+  StatsPullerManagerImpl& mPullerManager = StatsPullerManagerImpl::GetInstance();
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
similarity index 62%
rename from cmds/statsd/src/external/StatsPullerManager.cpp
rename to cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 43543cc..07d0b3e 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -21,9 +21,11 @@
 #include <cutils/log.h>
 #include <algorithm>
 #include <climits>
+#include "CpuTimePerUidFreqPuller.h"
+#include "CpuTimePerUidPuller.h"
 #include "ResourcePowerManagerPuller.h"
 #include "StatsCompanionServicePuller.h"
-#include "StatsPullerManager.h"
+#include "StatsPullerManagerImpl.h"
 #include "StatsService.h"
 #include "logd/LogEvent.h"
 #include "statslog.h"
@@ -35,33 +37,42 @@
 using std::shared_ptr;
 using std::string;
 using std::vector;
+using std::list;
 
 namespace android {
 namespace os {
 namespace statsd {
 
-StatsPullerManager::StatsPullerManager()
+StatsPullerManagerImpl::StatsPullerManagerImpl()
     : mCurrentPullingInterval(LONG_MAX), mPullStartTimeMs(get_pull_start_time_ms()) {
-    shared_ptr<StatsPuller> statsCompanionServicePuller =
-            make_shared<StatsCompanionServicePuller>();
+    shared_ptr<StatsPuller> statsCompanionServicePuller = make_shared<StatsCompanionServicePuller>();
     shared_ptr<StatsPuller> resourcePowerManagerPuller = make_shared<ResourcePowerManagerPuller>();
+    shared_ptr<StatsPuller> cpuTimePerUidPuller = make_shared<CpuTimePerUidPuller>();
+    shared_ptr<StatsPuller> cpuTimePerUidFreqPuller = make_shared<CpuTimePerUidFreqPuller>();
 
-    mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED, statsCompanionServicePuller});
-    mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED, statsCompanionServicePuller});
-    mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED, statsCompanionServicePuller});
-    mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
-    mPullers.insert(
-            {android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
-    mPullers.insert(
-            {android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
-    mPullers.insert({android::util::POWER_STATE_VOTER_PULLED, resourcePowerManagerPuller});
-    mPullers.insert(
-            {android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
+    mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED,
+                     statsCompanionServicePuller});
+    mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED,
+                     statsCompanionServicePuller});
+    mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED,
+                     statsCompanionServicePuller});
+    mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG,
+                     statsCompanionServicePuller});
+    mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG,
+                     statsCompanionServicePuller});
+    mPullers.insert({android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED,
+                     resourcePowerManagerPuller});
+    mPullers.insert({android::util::POWER_STATE_VOTER_PULLED,
+                     resourcePowerManagerPuller});
+    mPullers.insert({android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED,
+                     resourcePowerManagerPuller});
+    mPullers.insert({android::util::CPU_TIME_PER_UID_PULLED, cpuTimePerUidPuller});
+    mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ_PULLED, cpuTimePerUidFreqPuller});
 
     mStatsCompanionService = StatsService::getStatsCompanionService();
 }
 
-bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
     if (DEBUG) ALOGD("Initiating pulling %d", tagId);
 
     if (mPullers.find(tagId) != mPullers.end()) {
@@ -72,26 +83,26 @@
     }
 }
 
-StatsPullerManager& StatsPullerManager::GetInstance() {
-    static StatsPullerManager instance;
+StatsPullerManagerImpl& StatsPullerManagerImpl::GetInstance() {
+    static StatsPullerManagerImpl instance;
     return instance;
 }
 
-bool StatsPullerManager::PullerForMatcherExists(int tagId) {
+bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) {
     return mPullers.find(tagId) != mPullers.end();
 }
 
-long StatsPullerManager::get_pull_start_time_ms() {
+long StatsPullerManagerImpl::get_pull_start_time_ms() {
     // TODO: limit and align pull intervals to 10min boundaries if this turns out to be a problem
     return time(nullptr) * 1000;
 }
 
-void StatsPullerManager::RegisterReceiver(int tagId, sp<PullDataReceiver> receiver,
-                                          long intervalMs) {
+void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
+                                              long intervalMs) {
     AutoMutex _l(mReceiversLock);
-    vector<ReceiverInfo>& receivers = mReceivers[tagId];
+    auto& receivers = mReceivers[tagId];
     for (auto it = receivers.begin(); it != receivers.end(); it++) {
-        if (it->receiver.get() == receiver.get()) {
+        if (it->receiver == receiver) {
             VLOG("Receiver already registered of %d", (int)receivers.size());
             return;
         }
@@ -114,7 +125,7 @@
     VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
 }
 
-void StatsPullerManager::UnRegisterReceiver(int tagId, sp<PullDataReceiver> receiver) {
+void StatsPullerManagerImpl::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
     AutoMutex _l(mReceiversLock);
     if (mReceivers.find(tagId) == mReceivers.end()) {
         VLOG("Unknown pull code or no receivers: %d", tagId);
@@ -122,7 +133,7 @@
     }
     auto& receivers = mReceivers.find(tagId)->second;
     for (auto it = receivers.begin(); it != receivers.end(); it++) {
-        if (receiver.get() == it->receiver.get()) {
+        if (receiver == it->receiver) {
             receivers.erase(it);
             VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size());
             return;
@@ -130,7 +141,7 @@
     }
 }
 
-void StatsPullerManager::OnAlarmFired() {
+void StatsPullerManagerImpl::OnAlarmFired() {
     AutoMutex _l(mReceiversLock);
 
     uint64_t currentTimeMs = time(nullptr) * 1000;
@@ -155,8 +166,13 @@
         vector<shared_ptr<LogEvent>> data;
         if (Pull(pullInfo.first, &data)) {
             for (const auto& receiverInfo : pullInfo.second) {
-                receiverInfo->receiver->onDataPulled(data);
-                receiverInfo->timeInfo.second = currentTimeMs;
+                sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
+                if (receiverPtr != nullptr) {
+                    receiverPtr->onDataPulled(data);
+                    receiverInfo->timeInfo.second = currentTimeMs;
+                } else {
+                    VLOG("receiver already gone.");
+                }
             }
         }
     }
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
new file mode 100644
index 0000000..0b9f21e
--- /dev/null
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IServiceManager.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include <list>
+#include "PullDataReceiver.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StatsPullerManagerImpl : public virtual RefBase {
+public:
+    static StatsPullerManagerImpl& GetInstance();
+
+    void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs);
+
+    void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
+
+    // Verify if we know how to pull for this matcher
+    bool PullerForMatcherExists(int tagId);
+
+    void OnAlarmFired();
+
+    bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data);
+
+private:
+    StatsPullerManagerImpl();
+
+    // use this to update alarm
+    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+    sp<IStatsCompanionService> get_stats_companion_service();
+
+    // mapping from simple matcher tagId to puller
+    std::map<int, std::shared_ptr<StatsPuller>> mPullers;
+
+    typedef struct {
+        // pull_interval_sec : last_pull_time_sec
+        std::pair<uint64_t, uint64_t> timeInfo;
+        wp<PullDataReceiver> receiver;
+    } ReceiverInfo;
+
+    // mapping from simple matcher tagId to receivers
+    std::map<int, std::list<ReceiverInfo>> mReceivers;
+
+    Mutex mReceiversLock;
+
+    long mCurrentPullingInterval;
+
+    // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
+    // bucket size. All pulled metrics start pulling based on this time, so that they can be
+    // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
+    // request time.
+    const long mPullStartTimeMs;
+
+    long get_pull_start_time_ms();
+
+    LogEvent parse_pulled_data(String16 data);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
index 006d74c..adb691e 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -22,7 +22,6 @@
 #include <unordered_map>
 #include <vector>
 #include "LogMatchingTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
 namespace android {
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index d82da3b..ffbf248 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -17,7 +17,6 @@
 #ifndef LOG_MATCHING_TRACKER_H
 #define LOG_MATCHING_TRACKER_H
 
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "logd/LogEvent.h"
 #include "matchers/matcher_util.h"
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
index e110ec8..5dca55e 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -23,7 +23,6 @@
 #include <unordered_map>
 #include <vector>
 #include "LogMatchingTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
 namespace android {
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 6aa2211..cccc9b3 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -16,7 +16,6 @@
 
 #include "Log.h"
 
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "matchers/LogMatchingTracker.h"
 #include "matchers/matcher_util.h"
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
deleted file mode 100644
index 7aa748f..0000000
--- a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG true  // STOPSHIP if true
-#include "Log.h"
-
-#include "CountAnomalyTracker.h"
-
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-CountAnomalyTracker::CountAnomalyTracker(const Alert& alert)
-    : mAlert(alert),
-      mNumPastBuckets(alert.number_of_buckets() > 0 ? alert.number_of_buckets() - 1 : 0),
-      mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr) {
-
-    VLOG("CountAnomalyTracker() called");
-    if (alert.number_of_buckets() < 1) {
-        ALOGE("Cannot create CountAnomalyTracker with %d buckets", alert.number_of_buckets());
-    }
-    reset(); // initialization
-}
-
-CountAnomalyTracker::~CountAnomalyTracker() {
-    VLOG("~CountAnomalyTracker() called");
-}
-
-void CountAnomalyTracker::addPastBucket(int pastBucketCount,
-                                        time_t numberOfBucketsAgo) {
-    VLOG("addPastBucket() called.");
-    if (numberOfBucketsAgo < 1) {
-        ALOGE("Cannot add a past bucket %ld units in past", numberOfBucketsAgo);
-        return;
-    }
-    // If past bucket was ancient, just empty out all past info.
-    // This always applies if mNumPastBuckets == 0 (i.e. store no past buckets).
-    if (numberOfBucketsAgo > (time_t) mNumPastBuckets) {
-        reset();
-        return;
-    }
-
-    // Empty out old mPastBuckets[i] values and update mSumPastCounters.
-    for (size_t i = mOldestBucketIndex;
-                        i < mOldestBucketIndex + numberOfBucketsAgo; i++) {
-        mSumPastCounters -= mPastBuckets[index(i)];
-        mPastBuckets[index(i)] = 0;
-    }
-
-    // Replace the oldest bucket with the new bucket we are adding.
-    mPastBuckets[mOldestBucketIndex] = pastBucketCount;
-    mSumPastCounters += pastBucketCount;
-
-    // Advance the oldest bucket index by numberOfBucketsAgo units.
-    mOldestBucketIndex = index(mOldestBucketIndex + numberOfBucketsAgo);
-
-    // TODO: Once dimensions are added to mSumPastCounters:
-    // iterate through mSumPastCounters and remove any entries that are 0.
-}
-
-void CountAnomalyTracker::reset() {
-    VLOG("reset() called.");
-    for (size_t i = 0; i < mNumPastBuckets; i++) {
-        mPastBuckets[i] = 0;
-    }
-    mSumPastCounters = 0;
-    mOldestBucketIndex = 0;
-}
-
-void CountAnomalyTracker::checkAnomaly(int currentCount) {
-    // Skip the check if in refractory period.
-    if (time(nullptr) < mRefractoryPeriodEndsSec) {
-        VLOG("Skipping anomaly check since within refractory period");
-        return;
-    }
-
-    // TODO: Remove these extremely verbose debugging log.
-    VLOG("Checking whether %d + %d > %lld",
-         mSumPastCounters, currentCount, mAlert.trigger_if_sum_gt());
-
-    // Note that this works even if mNumPastBuckets < 1 (since then
-    // mSumPastCounters = 0 so the comparison is based only on currentCount).
-    if (mAlert.has_trigger_if_sum_gt() &&
-            mSumPastCounters + currentCount > mAlert.trigger_if_sum_gt()) {
-        declareAnomaly();
-    }
-}
-
-void CountAnomalyTracker::declareAnomaly() {
-    // TODO(guardrail): Consider guarding against too short refractory periods.
-    time_t currTime = time(nullptr);
-    mRefractoryPeriodEndsSec = currTime + mAlert.refractory_period_secs();
-
-    // TODO: If we had access to the bucket_size_millis, consider calling reset()
-    // if (mAlert.refractory_period_secs() > mNumPastBuckets * bucket_size_millis * 1000).
-
-    if (mAlert.has_incidentd_details()) {
-        const Alert_IncidentdDetails& incident = mAlert.incidentd_details();
-        if (incident.has_alert_name()) {
-            ALOGW("An anomaly (%s) has occurred! Informing incidentd.",
-                  incident.alert_name().c_str());
-        } else {
-            // TODO: Can construct a name based on the criteria (and/or relay the criteria).
-            ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
-        }
-        // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
-    } else {
-        ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
-    }
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.h b/cmds/statsd/src/metrics/CountAnomalyTracker.h
deleted file mode 100644
index 79c47d2a..0000000
--- a/cmds/statsd/src/metrics/CountAnomalyTracker.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef COUNT_ANOMALY_TRACKER_H
-#define COUNT_ANOMALY_TRACKER_H
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
-
-#include <memory> // unique_ptr
-#include <stdlib.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// TODO: Can probably be used for Count, Value, and Gauge. If so, rename to ValueAnomalyTracker.
-// (caveat: currently, the value cannot be negative. Probably fine for P.)
-class CountAnomalyTracker {
-public:
-    CountAnomalyTracker(const Alert& alert);
-
-    virtual ~CountAnomalyTracker();
-
-
-    // Adds a new past bucket, holding pastBucketCount, and then advances the
-    // present by numberOfBucketsAgo buckets (filling any intervening buckets
-    // with 0s).
-    // Thus, the newly added bucket (which holds pastBucketCount) is stored
-    // numberOfBucketsAgo buckets ago.
-    void addPastBucket(int pastBucketCount, time_t numberOfBucketsAgo);
-
-    // Informs the anomaly tracker of the current bucket's count, so that it can
-    // determine whether an anomaly has occurred. This value is not stored.
-    void checkAnomaly(int currentCount);
-
-private:
-    // statsd_config.proto Alert message that defines this tracker.
-    const Alert mAlert;
-
-    // Number of past buckets. One less than the total number of buckets needed
-    // for the anomaly detection (since the current bucket is not in the past).
-    const size_t mNumPastBuckets;
-
-    // Count values for each of the past mNumPastBuckets buckets.
-    // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
-    std::unique_ptr<int[]> mPastBuckets;
-
-    // Sum over all of mPastBuckets (cached).
-    // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
-    //       At that point, mSumPastCounters must never contain entries of 0.
-    int mSumPastCounters;
-
-    // Index of the oldest bucket (i.e. the next bucket to be overwritten).
-    size_t mOldestBucketIndex = 0;
-
-    // Timestamp that the refractory period (if this anomaly was declared) ends, in seconds.
-    // If an anomaly was never declared, set to 0.
-    time_t mRefractoryPeriodEndsSec = 0;
-
-    void declareAnomaly();
-
-    // Calculates the corresponding index within the circular array.
-    size_t index(size_t unsafeIndex) {
-        return unsafeIndex % mNumPastBuckets;
-    }
-
-    // Resets all data. For use when all the data gets stale.
-    void reset();
-};
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
-#endif  // COUNT_ANOMALY_TRACKER_H
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 94f4adf..f9da68e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -17,18 +17,20 @@
 #define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 
-#include "CountAnomalyTracker.h"
+#include "../anomaly/DiscreteAnomalyTracker.h"
 #include "CountMetricProducer.h"
 #include "stats_util.h"
 
 #include <limits.h>
 #include <stdlib.h>
 
+using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_INT64;
 using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
 using android::util::ProtoOutputStream;
 using std::map;
 using std::string;
@@ -40,7 +42,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_COUNT_METRICS = 5;
@@ -61,6 +63,7 @@
 const int FIELD_ID_COUNT = 3;
 
 // TODO: add back AnomalyTracker.
+
 CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
@@ -72,17 +75,6 @@
         mBucketSizeNs = LLONG_MAX;
     }
 
-    mAnomalyTrackers.reserve(metric.alerts_size());
-    for (int i = 0; i < metric.alerts_size(); i++) {
-        const Alert& alert = metric.alerts(i);
-        if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
-            mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert));
-        } else {
-            ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
-                  alert.trigger_if_sum_gt(), alert.number_of_buckets());
-        }
-    }
-
     // TODO: use UidMap if uid->pkg_name is required
     mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
 
@@ -94,7 +86,7 @@
 
     startNewProtoOutputStream(mStartTimeNs);
 
-    VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+    VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
@@ -104,7 +96,7 @@
 
 void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
     mProto = std::make_unique<ProtoOutputStream>();
-    mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+    mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
     mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
 }
@@ -113,17 +105,17 @@
 }
 
 void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
-    VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+    VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
 }
 
-StatsLogReport CountMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
     long long endTime = time(nullptr) * NS_PER_SEC;
 
     // Dump current bucket if it's stale.
     // If current bucket is still on-going, don't force dump current bucket.
     // In finish(), We can force dump current bucket.
     flushCounterIfNeeded(endTime);
-    VLOG("metric %lld dump report now...", mMetric.metric_id());
+    VLOG("metric %s dump report now...", mMetric.name().c_str());
 
     for (const auto& counter : mPastBuckets) {
         const HashableDimensionKey& hashableKey = counter.first;
@@ -133,11 +125,13 @@
             ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
             continue;
         }
-        long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+        long long wrapperToken =
+                mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension (KeyValuePairs).
         for (const auto& kv : it->second) {
-            long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+            long long dimensionToken =
+                    mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
             mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
             if (kv.has_value_str()) {
                 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -153,7 +147,8 @@
 
         // Then fill bucket_info (CountBucketInfo).
         for (const auto& bucket : counter.second) {
-            long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+            long long bucketInfoToken =
+                    mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
             mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
                           (long long)bucket.mBucketStartNs);
             mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -170,22 +165,20 @@
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
                   (long long)mCurrentBucketStartTimeNs);
 
-    VLOG("metric %lld dump report now...", mMetric.metric_id());
-    std::unique_ptr<uint8_t[]> buffer = serializeProto();
+    VLOG("metric %s dump report now...", mMetric.name().c_str());
+    std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(endTime);
     mPastBuckets.clear();
     mByteSize = 0;
 
-    // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
-    // return std::move(buffer);
-    return StatsLogReport();
+    return buffer;
 
     // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
 void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
-    VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+    VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
     mCondition = conditionMet;
 }
 
@@ -201,25 +194,19 @@
         return;
     }
 
-    auto it = mCurrentSlicedCounter.find(eventKey);
+    auto it = mCurrentSlicedCounter->find(eventKey);
 
-    if (it == mCurrentSlicedCounter.end()) {
+    if (it == mCurrentSlicedCounter->end()) {
         // create a counter for the new key
-        mCurrentSlicedCounter[eventKey] = 1;
-
+        (*mCurrentSlicedCounter)[eventKey] = 1;
     } else {
         // increment the existing value
         auto& count = it->second;
         count++;
     }
 
-    // TODO: Re-add anomaly detection (similar to):
-    // for (auto& tracker : mAnomalyTrackers) {
-    //     tracker->checkAnomaly(mCounter);
-    // }
-
-    VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
-         mCurrentSlicedCounter[eventKey]);
+    VLOG("metric %s %s->%d", mMetric.name().c_str(), eventKey.c_str(),
+         (*mCurrentSlicedCounter)[eventKey]);
 }
 
 // When a new matched event comes in, we check if event falls into the current
@@ -230,30 +217,32 @@
     }
 
     // adjust the bucket start time
-    int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+    // TODO: This (and addPastBucket to which it goes) doesn't really need to be an int64.
+    uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
 
     CountBucket info;
     info.mBucketStartNs = mCurrentBucketStartTimeNs;
     info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
-    for (const auto& counter : mCurrentSlicedCounter) {
+    for (const auto& counter : *mCurrentSlicedCounter) {
         info.mCount = counter.second;
         auto& bucketList = mPastBuckets[counter.first];
         bucketList.push_back(info);
-        VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
+        VLOG("metric %s, dump key value: %s -> %d", mMetric.name().c_str(), counter.first.c_str(),
              counter.second);
         mByteSize += sizeof(info);
     }
 
-    // TODO: Re-add anomaly detection (similar to):
-    // for (auto& tracker : mAnomalyTrackers) {
-    //     tracker->addPastBucket(mCounter, numBucketsForward);
-    //}
+    for (auto& tracker : mAnomalyTrackers) {
+        tracker->addOrUpdateBucket(mCurrentSlicedCounter, mCurrentBucketNum);
+        tracker->declareAndDeclareAnomaly();
+    }
 
-    // Reset counters
-    mCurrentSlicedCounter.clear();
+    // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
+    mCurrentSlicedCounter = std::make_shared<DimToValMap>();
 
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
-    VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+    mCurrentBucketNum += numBucketsForward;
+    VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index c3af006..b7e480c 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -23,14 +23,11 @@
 #include <gtest/gtest_prod.h>
 #include "../condition/ConditionTracker.h"
 #include "../matchers/matcher_util.h"
-#include "CountAnomalyTracker.h"
+#include "../anomaly/DiscreteAnomalyTracker.h"
 #include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
 
-using namespace std;
-
 namespace android {
 namespace os {
 namespace statsd {
@@ -54,7 +51,7 @@
     void finish() override;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
-    StatsLogReport onDumpReport() override;
+    std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
 
     void onSlicedConditionMayChange(const uint64_t eventTime) override;
 
@@ -82,9 +79,9 @@
     size_t mByteSize;
 
     // The current bucket.
-    std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter;
+    std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
 
-    vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers;
+    vector<std::unique_ptr<DiscreteAnomalyTracker>> mAnomalyTrackers;
 
     void flushCounterIfNeeded(const uint64_t newEventTime);
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c0a0d98..aaf3ec2 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -23,11 +23,13 @@
 #include <limits.h>
 #include <stdlib.h>
 
+using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_INT64;
 using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
 using android::util::ProtoOutputStream;
 using std::string;
 using std::unordered_map;
@@ -38,7 +40,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_DURATION_METRICS = 6;
@@ -90,7 +92,7 @@
 
     startNewProtoOutputStream(mStartTimeNs);
 
-    VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+    VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
@@ -100,7 +102,7 @@
 
 void DurationMetricProducer::startNewProtoOutputStream(long long startTime) {
     mProto = std::make_unique<ProtoOutputStream>();
-    mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+    mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
     mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
 }
@@ -125,7 +127,7 @@
 }
 
 void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
-    VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+    VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
     // Now for each of the on-going event, check if the condition has changed for them.
     flushIfNeeded(eventTime);
     for (auto& pair : mCurrentSlicedDuration) {
@@ -134,7 +136,7 @@
 }
 
 void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
-    VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+    VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
     mCondition = conditionMet;
     // TODO: need to populate the condition change time from the event which triggers the condition
     // change, instead of using current time.
@@ -159,14 +161,14 @@
     }
 }
 
-StatsLogReport DurationMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
     long long endTime = time(nullptr) * NS_PER_SEC;
 
     // Dump current bucket if it's stale.
     // If current bucket is still on-going, don't force dump current bucket.
     // In finish(), We can force dump current bucket.
     flushIfNeeded(endTime);
-    VLOG("metric %lld dump report now...", mMetric.metric_id());
+    VLOG("metric %s dump report now...", mMetric.name().c_str());
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
@@ -176,11 +178,13 @@
             ALOGW("Dimension key %s not found?!?! skip...", hashableKey.c_str());
             continue;
         }
-        long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+        long long wrapperToken =
+                mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension (KeyValuePairs).
         for (const auto& kv : it->second) {
-            long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+            long long dimensionToken =
+                    mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
             mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
             if (kv.has_value_str()) {
                 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -196,7 +200,8 @@
 
         // Then fill bucket_info (DurationBucketInfo).
         for (const auto& bucket : pair.second) {
-            long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+            long long bucketInfoToken =
+                    mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
             mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
                           (long long)bucket.mBucketStartNs);
             mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -214,14 +219,12 @@
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
                   (long long)mCurrentBucketStartTimeNs);
 
-    std::unique_ptr<uint8_t[]> buffer = serializeProto();
+    std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(endTime);
     mPastBuckets.clear();
 
-    // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
-    // return std::move(buffer);
-    return StatsLogReport();
+    return buffer;
 }
 
 void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 8fdd0d4..eea00454 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -26,7 +26,6 @@
 #include "duration_helper/DurationTracker.h"
 #include "duration_helper/MaxDurationTracker.h"
 #include "duration_helper/OringDurationTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
 
@@ -50,7 +49,7 @@
     void finish() override;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
-    StatsLogReport onDumpReport() override;
+    std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
 
     void onSlicedConditionMayChange(const uint64_t eventTime) override;
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index ee0bfde..b2ffb2f 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -23,10 +23,12 @@
 #include <limits.h>
 #include <stdlib.h>
 
+using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_STRING;
 using android::util::FIELD_TYPE_MESSAGE;
 using android::util::ProtoOutputStream;
 using std::map;
@@ -39,7 +41,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_EVENT_METRICS = 4;
@@ -61,7 +63,7 @@
 
     startNewProtoOutputStream(mStartTimeNs);
 
-    VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+    VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
@@ -73,7 +75,7 @@
     mProto = std::make_unique<ProtoOutputStream>();
     // TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData,
     // and StatsEvent.
-    mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+    mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
     mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS);
 }
@@ -84,24 +86,23 @@
 void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
 }
 
-StatsLogReport EventMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() {
     long long endTime = time(nullptr) * NS_PER_SEC;
     mProto->end(mProtoToken);
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
 
     size_t bufferSize = mProto->size();
-    VLOG("metric %lld dump report now... proto size: %zu ", mMetric.metric_id(), bufferSize);
-    std::unique_ptr<uint8_t[]> buffer = serializeProto();
+    VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
+    std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(endTime);
+    mByteSize = 0;
 
-    // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
-    // return std::move(buffer);
-    return StatsLogReport();
+    return buffer;
 }
 
 void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
-    VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+    VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
     mCondition = conditionMet;
 }
 
@@ -113,16 +114,18 @@
         return;
     }
 
-    long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+    long long wrapperToken =
+            mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs());
     long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_STATS_EVENTS);
     event.ToProto(*mProto);
     mProto->end(eventToken);
     mProto->end(wrapperToken);
+    // TODO: Find a proper way to derive the size of incoming LogEvent.
 }
 
 size_t EventMetricProducer::byteSize() {
-    return mProto->size();
+    return mByteSize;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 2ca8181..7740621 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -24,7 +24,6 @@
 #include "../condition/ConditionTracker.h"
 #include "../matchers/matcher_util.h"
 #include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
 
@@ -50,7 +49,7 @@
     void finish() override;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
-    StatsLogReport onDumpReport() override;
+    std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
 
     void onSlicedConditionMayChange(const uint64_t eventTime) override;
 
@@ -66,6 +65,8 @@
 
 private:
     const EventMetric mMetric;
+
+    size_t mByteSize;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index dcfcc19..ed18f89 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -24,11 +24,13 @@
 #include <limits.h>
 #include <stdlib.h>
 
+using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_INT64;
 using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
 using android::util::ProtoOutputStream;
 using std::map;
 using std::string;
@@ -40,7 +42,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_GAUGE_METRICS = 8;
@@ -88,7 +90,7 @@
 
     startNewProtoOutputStream(mStartTimeNs);
 
-    VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+    VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
@@ -98,7 +100,7 @@
 
 void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) {
     mProto = std::make_unique<ProtoOutputStream>();
-    mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+    mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
     mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
 }
@@ -106,8 +108,8 @@
 void GaugeMetricProducer::finish() {
 }
 
-StatsLogReport GaugeMetricProducer::onDumpReport() {
-    VLOG("gauge metric %lld dump report now...", mMetric.metric_id());
+std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
+    VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
 
     // Dump current bucket if it's stale.
     // If current bucket is still on-going, don't force dump current bucket.
@@ -123,11 +125,13 @@
         }
 
         VLOG("  dimension key %s", hashableKey.c_str());
-        long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+        long long wrapperToken =
+                mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension (KeyValuePairs).
         for (const auto& kv : it->second) {
-            long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+            long long dimensionToken =
+                    mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
             mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
             if (kv.has_value_str()) {
                 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -143,7 +147,8 @@
 
         // Then fill bucket_info (GaugeBucketInfo).
         for (const auto& bucket : pair.second) {
-            long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+            long long bucketInfoToken =
+                    mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
             mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
                           (long long)bucket.mBucketStartNs);
             mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -159,22 +164,20 @@
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
                   (long long)mCurrentBucketStartTimeNs);
 
-    std::unique_ptr<uint8_t[]> buffer = serializeProto();
+    std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
     mPastBuckets.clear();
     mByteSize = 0;
 
-    // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
-    // return std::move(buffer);
-    return StatsLogReport();
+    return buffer;
 
     // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
 void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
     AutoMutex _l(mLock);
-    VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+    VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
     mCondition = conditionMet;
 
     // Push mode. Nothing to do.
@@ -198,7 +201,7 @@
 }
 
 void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
-    VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+    VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
 }
 
 long GaugeMetricProducer::getGauge(const LogEvent& event) {
@@ -275,14 +278,14 @@
         bucketList.push_back(info);
         mByteSize += sizeof(info);
 
-        VLOG("gauge metric %lld, dump key value: %s -> %ld", mMetric.metric_id(),
+        VLOG("gauge metric %s, dump key value: %s -> %ld", mMetric.name().c_str(),
              slice.first.c_str(), slice.second);
     }
     // Reset counters
     mCurrentSlicedBucket.clear();
 
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
-    VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+    VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 3757174..f9e4deb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -24,7 +24,6 @@
 #include "../external/StatsPullerManager.h"
 #include "../matchers/matcher_util.h"
 #include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
 
@@ -60,7 +59,7 @@
     void finish() override;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
-    StatsLogReport onDumpReport() override;
+    std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
 
     size_t byteSize() override;
 
@@ -82,7 +81,7 @@
     static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000;
     const GaugeMetric mMetric;
 
-    StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+    StatsPullerManager mStatsPullerManager;
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 5ca83b2..62fb632 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -64,16 +64,16 @@
                               scheduledPull);
 }
 
-std::unique_ptr<uint8_t[]> MetricProducer::serializeProto() {
+std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProto() {
     size_t bufferSize = mProto->size();
 
-    std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]);
+    std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
 
     size_t pos = 0;
     auto it = mProto->data();
     while (it.readBuffer() != NULL) {
         size_t toRead = it.currentToRead();
-        std::memcpy(&buffer[pos], it.readBuffer(), toRead);
+        std::memcpy(&((*buffer)[pos]), it.readBuffer(), toRead);
         pos += toRead;
         it.rp()->move(toRead);
     }
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 7ac97a8..c0930e3 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -23,7 +23,6 @@
 
 #include <log/logprint.h>
 #include <utils/RefBase.h>
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 
 namespace android {
 namespace os {
@@ -39,6 +38,7 @@
                    const sp<ConditionWizard>& wizard)
         : mStartTimeNs(startTimeNs),
           mCurrentBucketStartTimeNs(startTimeNs),
+          mCurrentBucketNum(0),
           mCondition(conditionIndex >= 0 ? false : true),
           mConditionSliced(false),
           mWizard(wizard),
@@ -61,7 +61,7 @@
 
     // TODO: Pass a timestamp as a parameter in onDumpReport and update all its
     // implementations.
-    virtual StatsLogReport onDumpReport() = 0;
+    virtual std::unique_ptr<std::vector<uint8_t>> onDumpReport() = 0;
 
     virtual bool isConditionSliced() const {
         return mConditionSliced;
@@ -74,6 +74,8 @@
 
     uint64_t mCurrentBucketStartTimeNs;
 
+    uint64_t mCurrentBucketNum;
+
     int64_t mBucketSizeNs;
 
     bool mCondition;
@@ -118,7 +120,7 @@
 
     virtual void startNewProtoOutputStream(long long timestamp) = 0;
 
-    std::unique_ptr<uint8_t[]> serializeProto();
+    std::unique_ptr<std::vector<uint8_t>> serializeProto();
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 80b325f..e8a862f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -56,10 +56,10 @@
     }
 }
 
-vector<StatsLogReport> MetricsManager::onDumpReport() {
+vector<std::unique_ptr<vector<uint8_t>>> MetricsManager::onDumpReport() {
     VLOG("=========================Metric Reports Start==========================");
     // one StatsLogReport per MetricProduer
-    vector<StatsLogReport> reportList;
+    vector<std::unique_ptr<vector<uint8_t>>> reportList;
     for (auto& metric : mAllMetricProducers) {
         reportList.push_back(metric->onDumpReport());
     }
@@ -80,8 +80,6 @@
         return;
     }
 
-    // Since at least one of the metrics is interested in this event, we parse it now.
-    ALOGD("%s", event.ToString().c_str());
     vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed);
 
     for (auto& matcher : mAllLogEntryMatchers) {
@@ -104,18 +102,17 @@
                                           ConditionState::kNotEvaluated);
     // A bitmap to track if a condition has changed value.
     vector<bool> changedCache(mAllConditionTrackers.size(), false);
-    vector<bool> slicedChangedCache(mAllConditionTrackers.size(), false);
     for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
         if (conditionToBeEvaluated[i] == false) {
             continue;
         }
         sp<ConditionTracker>& condition = mAllConditionTrackers[i];
         condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
-                                     changedCache, slicedChangedCache);
+                                     changedCache);
     }
 
     for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
-        if (changedCache[i] == false && slicedChangedCache[i] == false) {
+        if (changedCache[i] == false) {
             continue;
         }
         auto pair = mConditionToMetricMap.find(i);
@@ -124,14 +121,13 @@
             for (auto metricIndex : metricList) {
                 // metric cares about non sliced condition, and it's changed.
                 // Push the new condition to it directly.
-                if (!mAllMetricProducers[metricIndex]->isConditionSliced() && changedCache[i]) {
+                if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
                     mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
                                                                          eventTime);
                     // metric cares about sliced conditions, and it may have changed. Send
                     // notification, and the metric can query the sliced conditions that are
                     // interesting to it.
-                } else if (mAllMetricProducers[metricIndex]->isConditionSliced() &&
-                           slicedChangedCache[i]) {
+                } else if (mAllMetricProducers[metricIndex]->isConditionSliced()) {
                     mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(eventTime);
                 }
             }
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 63e2c33..39c79f9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -44,7 +44,7 @@
     void finish();
 
     // Config source owner can call onDumpReport() to get all the metrics collected.
-    std::vector<StatsLogReport> onDumpReport();
+    std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
 
     size_t byteSize();
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 41b7a5d..a35070b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -23,13 +23,16 @@
 #include <limits.h>
 #include <stdlib.h>
 
+using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_INT64;
 using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
 using android::util::ProtoOutputStream;
 using std::list;
+using std::make_pair;
 using std::make_shared;
 using std::map;
 using std::shared_ptr;
@@ -41,7 +44,7 @@
 namespace statsd {
 
 // for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
 const int FIELD_ID_START_REPORT_NANOS = 2;
 const int FIELD_ID_END_REPORT_NANOS = 3;
 const int FIELD_ID_VALUE_METRICS = 7;
@@ -61,13 +64,23 @@
 const int FIELD_ID_END_BUCKET_NANOS = 2;
 const int FIELD_ID_VALUE = 3;
 
+static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L;
+
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const uint64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric), mPullTagId(pullTagId) {
+                                         const uint64_t startTimeNs,
+                                         shared_ptr<StatsPullerManager> statsPullerManager)
+    : MetricProducer(startTimeNs, conditionIndex, wizard),
+      mMetric(metric),
+      mStatsPullerManager(statsPullerManager),
+      mPullTagId(pullTagId) {
     // TODO: valuemetric for pushed events may need unlimited bucket length
-    mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+    if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
+        mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+    } else {
+        mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
+    }
 
     mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
 
@@ -78,23 +91,35 @@
     }
 
     if (!metric.has_condition() && mPullTagId != -1) {
-        mStatsPullerManager.RegisterReceiver(mPullTagId, this,
-                                             metric.bucket().bucket_size_millis());
+        VLOG("Setting up periodic pulling for %d", mPullTagId);
+        mStatsPullerManager->RegisterReceiver(mPullTagId, this,
+                                              metric.bucket().bucket_size_millis());
     }
 
     startNewProtoOutputStream(mStartTimeNs);
 
-    VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+    VLOG("value metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
 }
 
+// for testing
+ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+                                         const sp<ConditionWizard>& wizard, const int pullTagId,
+                                         const uint64_t startTimeNs)
+    : ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs,
+                          make_shared<StatsPullerManager>()) {
+}
+
 ValueMetricProducer::~ValueMetricProducer() {
     VLOG("~ValueMetricProducer() called");
+    if (mPullTagId != -1) {
+        mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
+    }
 }
 
 void ValueMetricProducer::startNewProtoOutputStream(long long startTime) {
     mProto = std::make_unique<ProtoOutputStream>();
-    mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+    mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
     mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
 }
@@ -105,11 +130,11 @@
 }
 
 void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
-    VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+    VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
 }
 
-StatsLogReport ValueMetricProducer::onDumpReport() {
-    VLOG("metric %lld dump report now...", mMetric.metric_id());
+std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
+    VLOG("metric %s dump report now...", mMetric.name().c_str());
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
@@ -119,11 +144,13 @@
             ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
             continue;
         }
-        long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+        long long wrapperToken =
+                mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension (KeyValuePairs).
         for (const auto& kv : it->second) {
-            long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+            long long dimensionToken =
+                    mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
             mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
             if (kv.has_value_str()) {
                 mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -139,7 +166,8 @@
 
         // Then fill bucket_info (ValueBucketInfo).
         for (const auto& bucket : pair.second) {
-            long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+            long long bucketInfoToken =
+                    mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
             mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
                           (long long)bucket.mBucketStartNs);
             mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -155,37 +183,35 @@
     mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
                   (long long)mCurrentBucketStartTimeNs);
 
-    VLOG("metric %lld dump report now...", mMetric.metric_id());
-    std::unique_ptr<uint8_t[]> buffer = serializeProto();
+    VLOG("metric %s dump report now...", mMetric.name().c_str());
+    std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
     mPastBuckets.clear();
     mByteSize = 0;
 
-    // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
-    // return std::move(buffer);
-    return StatsLogReport();
+    return buffer;
 
     // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
 void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
+    AutoMutex _l(mLock);
     mCondition = condition;
 
     if (mPullTagId != -1) {
         if (mCondition == true) {
-            mStatsPullerManager.RegisterReceiver(mPullTagId, this,
-                                                 mMetric.bucket().bucket_size_millis());
-        } else if (mCondition == ConditionState::kFalse) {
-            mStatsPullerManager.UnRegisterReceiver(mPullTagId, this);
+            mStatsPullerManager->RegisterReceiver(mPullTagId, this,
+                                                  mMetric.bucket().bucket_size_millis());
+        } else if (mCondition == false) {
+            mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
         }
 
         vector<shared_ptr<LogEvent>> allData;
-        if (mStatsPullerManager.Pull(mPullTagId, &allData)) {
+        if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
             if (allData.size() == 0) {
                 return;
             }
-            AutoMutex _l(mLock);
             for (const auto& data : allData) {
                 onMatchedLogEvent(0, *data, false);
             }
@@ -196,12 +222,16 @@
 }
 
 void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
-    if (mCondition == ConditionState::kTrue || !mMetric.has_condition()) {
-        AutoMutex _l(mLock);
+    AutoMutex _l(mLock);
+    if (mCondition == true || !mMetric.has_condition()) {
         if (allData.size() == 0) {
             return;
         }
         uint64_t eventTime = allData.at(0)->GetTimestampNs();
+        // alarm is not accurate and might drift.
+        if (eventTime > mCurrentBucketStartTimeNs + mBucketSizeNs * 3 / 2) {
+            flush_if_needed(eventTime);
+        }
         for (const auto& data : allData) {
             onMatchedLogEvent(0, *data, true);
         }
@@ -224,24 +254,36 @@
 
     long value = get_value(event);
 
-    if (scheduledPull) {
-        if (interval.raw.size() > 0) {
-            interval.raw.back().second = value;
-        } else {
-            interval.raw.push_back(std::make_pair(value, value));
-        }
-        mNextSlicedBucket[eventKey].raw[0].first = value;
-    } else {
-        if (mCondition == ConditionState::kTrue) {
-            interval.raw.push_back(std::make_pair(value, 0));
-        } else {
-            if (interval.raw.size() != 0) {
+    if (mPullTagId != -1) {
+        if (scheduledPull) {
+            // scheduled pull always sets beginning of current bucket and end
+            // of next bucket
+            if (interval.raw.size() > 0) {
                 interval.raw.back().second = value;
+            } else {
+                interval.raw.push_back(make_pair(value, value));
+            }
+            Interval& nextInterval = mNextSlicedBucket[eventKey];
+            if (nextInterval.raw.size() == 0) {
+                nextInterval.raw.push_back(make_pair(value, 0));
+            } else {
+                nextInterval.raw.front().first = value;
+            }
+        } else {
+            if (mCondition == true) {
+                interval.raw.push_back(make_pair(value, 0));
+            } else {
+                if (interval.raw.size() != 0) {
+                    interval.raw.back().second = value;
+                } else {
+                    interval.tainted = true;
+                    VLOG("Data on condition true missing!");
+                }
             }
         }
-    }
-    if (mPullTagId == -1) {
+    } else {
         flush_if_needed(eventTimeNs);
+        interval.raw.push_back(make_pair(value, 0));
     }
 }
 
@@ -251,7 +293,7 @@
     if (err == NO_ERROR) {
         return val;
     } else {
-        VLOG("Can't find value in message.");
+        VLOG("Can't find value in message. %s", event.ToString().c_str());
         return 0;
     }
 }
@@ -269,13 +311,21 @@
     info.mBucketStartNs = mCurrentBucketStartTimeNs;
     info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
 
+    int tainted = 0;
     for (const auto& slice : mCurrentSlicedBucket) {
         long value = 0;
-        for (const auto& pair : slice.second.raw) {
-            value += pair.second - pair.first;
+        if (mPullTagId != -1) {
+            for (const auto& pair : slice.second.raw) {
+                value += (pair.second - pair.first);
+            }
+        } else {
+            for (const auto& pair : slice.second.raw) {
+                value += pair.first;
+            }
         }
+        tainted += slice.second.tainted;
         info.mValue = value;
-        VLOG(" %s, %ld", slice.first.c_str(), value);
+        VLOG(" %s, %ld, %d", slice.first.c_str(), value, tainted);
         // it will auto create new vector of ValuebucketInfo if the key is not found.
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
@@ -290,7 +340,7 @@
         VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
     }
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
-    VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+    VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 8437665..c6c87f5 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -16,14 +16,13 @@
 
 #pragma once
 
+#include <gtest/gtest_prod.h>
 #include <utils/threads.h>
 #include <list>
 #include "../condition/ConditionTracker.h"
 #include "../external/PullDataReceiver.h"
 #include "../external/StatsPullerManager.h"
-#include "CountAnomalyTracker.h"
 #include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
 namespace android {
@@ -49,7 +48,7 @@
     void finish() override;
 
     // TODO: Pass a timestamp as a parameter in onDumpReport.
-    StatsLogReport onDumpReport() override;
+    std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
 
     void onSlicedConditionMayChange(const uint64_t eventTime);
 
@@ -73,7 +72,13 @@
 private:
     const ValueMetric mMetric;
 
-    StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+    std::shared_ptr<StatsPullerManager> mStatsPullerManager;
+
+    // for testing
+    ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
+                        const sp<ConditionWizard>& wizard, const int pullTagId,
+                        const uint64_t startTimeNs,
+                        std::shared_ptr<StatsPullerManager> statsPullerManager);
 
     Mutex mLock;
 
@@ -83,6 +88,7 @@
     // internal state of a bucket.
     typedef struct {
         std::vector<std::pair<long, long>> raw;
+        bool tainted;
     } Interval;
 
     std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
@@ -99,6 +105,10 @@
     void flush_if_needed(const uint64_t eventTimeNs);
 
     size_t mByteSize;
+
+    FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
+    FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index aeb234d..5c76d0e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -20,8 +20,6 @@
 #include "condition/ConditionWizard.h"
 #include "stats_util.h"
 
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-
 namespace android {
 namespace os {
 namespace statsd {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index a4d3098..43c21a8 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -173,7 +173,6 @@
 }
 
 void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
-    //  VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
     // Now for each of the on-going event, check if the condition has changed for them.
     for (auto& pair : mInfos) {
         if (pair.second.state == kStopped) {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index de53d62..b095884 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -19,8 +19,6 @@
 
 #include "DurationTracker.h"
 
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-
 namespace android {
 namespace os {
 namespace statsd {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index ca9cdfb..c2044d8 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -195,7 +195,7 @@
     const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
                                 config.event_metric_size() + config.value_metric_size();
     allMetricProducers.reserve(allMetricsCount);
-    StatsPullerManager& statsPullerManager = StatsPullerManager::GetInstance();
+    StatsPullerManager statsPullerManager;
     uint64_t startTimeNs = time(nullptr) * NS_PER_SEC;
 
     // Build MetricProducers for each metric defined in config.
@@ -203,7 +203,7 @@
     for (int i = 0; i < config.count_metric_size(); i++) {
         const CountMetric& metric = config.count_metric(i);
         if (!metric.has_what()) {
-            ALOGW("cannot find what in CountMetric %lld", metric.metric_id());
+            ALOGW("cannot find what in CountMetric %s", metric.name().c_str());
             return false;
         }
 
@@ -293,8 +293,8 @@
     for (int i = 0; i < config.event_metric_size(); i++) {
         int metricIndex = allMetricProducers.size();
         const EventMetric& metric = config.event_metric(i);
-        if (!metric.has_metric_id() || !metric.has_what()) {
-            ALOGW("cannot find the metric id or what in config");
+        if (!metric.has_name() || !metric.has_what()) {
+            ALOGW("cannot find the metric name or what in config");
             return false;
         }
         int trackerIndex;
@@ -320,7 +320,7 @@
     for (int i = 0; i < config.value_metric_size(); i++) {
         const ValueMetric& metric = config.value_metric(i);
         if (!metric.has_what()) {
-            ALOGW("cannot find what in ValueMetric %lld", metric.metric_id());
+            ALOGW("cannot find what in ValueMetric %s", metric.name().c_str());
             return false;
         }
 
@@ -360,7 +360,7 @@
     for (int i = 0; i < config.gauge_metric_size(); i++) {
         const GaugeMetric& metric = config.gauge_metric(i);
         if (!metric.has_what()) {
-            ALOGW("cannot find what in ValueMetric %lld", metric.metric_id());
+            ALOGW("cannot find what in ValueMetric %s", metric.name().c_str());
             return false;
         }
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index e089d065..edf3af0 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -21,7 +21,7 @@
 #include <vector>
 
 #include "../condition/ConditionTracker.h"
-#include "../external/StatsPullerManager.h"
+#include "../external/StatsPullerManagerImpl.h"
 #include "../matchers/LogMatchingTracker.h"
 
 namespace android {
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index d83c3a4..2398814 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -52,7 +52,7 @@
 
 void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
                        const vector<String16>& packageName) {
-    updateMap(time(nullptr) * 1000000000, uid, versionCode, packageName);
+    updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName);
 }
 
 void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
@@ -76,7 +76,7 @@
 }
 
 void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) {
-    updateApp(time(nullptr) * 1000000000, app_16, uid, versionCode);
+    updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode);
 }
 
 void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
@@ -112,7 +112,7 @@
 }
 
 void UidMap::removeApp(const String16& app_16, const int32_t& uid) {
-    removeApp(time(nullptr) * 1000000000, app_16, uid);
+    removeApp(time(nullptr) * NS_PER_SEC, app_16, uid);
 }
 void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
     lock_guard<mutex> lock(mMutex);
@@ -150,6 +150,31 @@
     mSubscribers.erase(producer);
 }
 
+void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) {
+    lock_guard<mutex> lock(mIsolatedMutex);
+
+    mIsolatedUidMap[isolatedUid] = parentUid;
+}
+
+void UidMap::removeIsolatedUid(int isolatedUid, int parentUid) {
+    lock_guard<mutex> lock(mIsolatedMutex);
+
+    auto it = mIsolatedUidMap.find(isolatedUid);
+    if (it != mIsolatedUidMap.end()) {
+        mIsolatedUidMap.erase(it);
+    }
+}
+
+int UidMap::getParentUidOrSelf(int uid) {
+    lock_guard<mutex> lock(mIsolatedMutex);
+
+    auto it = mIsolatedUidMap.find(uid);
+    if (it != mIsolatedUidMap.end()) {
+        return it->second;
+    }
+    return uid;
+}
+
 void UidMap::clearOutput() {
     mOutput.Clear();
 
@@ -177,7 +202,7 @@
 }
 
 UidMapping UidMap::getOutput(const ConfigKey& key) {
-    return getOutput(time(nullptr) * 1000000000, key);
+    return getOutput(time(nullptr) * NS_PER_SEC, key);
 }
 
 UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) {
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index bf120e0..de68fbc 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -84,6 +84,12 @@
     // Informs uid map that a config is removed. Used for keeping mConfigKeys up to date.
     void OnConfigRemoved(const ConfigKey& key);
 
+    void assignIsolatedUid(int isolatedUid, int parentUid);
+    void removeIsolatedUid(int isolatedUid, int parentUid);
+
+    // Returns the parent uid if it exists. Otherwise, returns the same uid that was passed-in.
+    int getParentUidOrSelf(int uid);
+
     // Gets the output. If every config key has received the output, then the output is cleared.
     UidMapping getOutput(const ConfigKey& key);
 
@@ -105,11 +111,16 @@
 
     // TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
     mutable mutex mMutex;
+    mutable mutex mIsolatedMutex;
 
     // Maps uid to application data. This must be multimap since there is a feature in Android for
     // multiple apps to share the same uid.
     std::unordered_multimap<int, AppData> mMap;
 
+    // Maps isolated uid to the parent uid. Any metrics for an isolated uid will instead contribute
+    // to the parent uid.
+    std::unordered_map<int, int> mIsolatedUidMap;
+
     // We prepare the output proto as apps are updated, so that we can grab the current output.
     UidMapping mOutput;
 
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto
index 82d9759..9ab07de 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -72,7 +72,7 @@
         PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
         SettingChanged setting_changed = 41;
         ActivityForegroundStateChanged activity_foreground_state_changed = 42;
-
+        IsolatedUidChanged isolated_uid_changed = 43;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -86,6 +86,9 @@
         PowerStatePlatformSleepStatePulled power_state_platform_sleep_state_pulled = 1005;
         PowerStateVoterPulled power_state_voter_pulled = 1006;
         PowerStateSubsystemSleepStatePulled power_state_subsystem_sleep_state_pulled = 1007;
+        CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008;
+        CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009;
+        CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
     }
 }
 
@@ -695,7 +698,6 @@
     optional int32 user = 7;
 }
 
-
 /*
  * Logs activity going to foreground or background
  *
@@ -848,3 +850,62 @@
     optional uint64 last_entry_timestamp_ms = 5;
     optional bool supported_only_in_suspend = 6;
 }
+
+/**
+<<<<<<< HEAD
+ * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky
+ * behavior in its own uid. However, the metrics of these isolated uid's almost always should be
+ * attributed back to the parent (host) uid. One example is Chrome.
+ *
+ * Logged from:
+ *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message IsolatedUidChanged {
+    // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
+    optional int32 parent_uid = 1;
+
+    optional int32 isolated_uid = 2;
+
+    // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to
+    // be removed before if it's used for another parent uid.
+    optional int32 is_create = 3;
+}
+
+/*
+<<<<<<< HEAD
+ * Pulls Cpu time per frequency.
+ * Note: this should be pulled for gauge metric only, without condition.
+ * The puller keeps internal state of last values. It should not be pulled by
+ * different metrics.
+ * The pulled data is delta of cpu time from last pull, calculated as
+ * following:
+ * if current time is larger than last value, take delta between the two.
+ * if current time is smaller than last value, there must be a cpu
+ * hotplug event, and the current time is taken as delta.
+ */
+message CpuTimePerFreqPulled {
+    optional uint32 cluster = 1;
+    optional uint32 freq_index = 2;
+    optional uint64 time = 3;
+}
+
+/*
+ * Pulls Cpu Time Per Uid.
+ * Note that isolated process uid time should be attributed to host uids.
+ */
+message CpuTimePerUidPulled {
+    optional uint64 uid = 1;
+    optional uint64 user_time_ms = 2;
+    optional uint64 sys_time_ms = 3;
+}
+
+/**
+ * Pulls Cpu Time Per Uid per frequency.
+ * Note that isolated process uid time should be attributed to host uids.
+ * For each uid, we order the time by descending frequencies.
+ */
+message CpuTimePerUidFreqPulled {
+    optional uint64 uid = 1;
+    optional uint64 freq_idx = 2;
+    optional uint64 time_ms = 3;
+}
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index a9507bf..e1d0aceb 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -13,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef STATS_UTIL_H
-#define STATS_UTIL_H
+
+#pragma once
 
 #include "logd/LogReader.h"
 #include "storage/DropboxWriter.h"
@@ -22,6 +22,8 @@
 #include <log/logprint.h>
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
+#include <unordered_map>
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -33,6 +35,10 @@
 
 typedef std::map<std::string, HashableDimensionKey> ConditionKey;
 
+// TODO: For P, change int to int64_t.
+// TODO: Should HashableDimensionKey be marked here as const?
+typedef std::unordered_map<HashableDimensionKey, int> DimToValMap;
+
 EventMetricData parse(log_msg msg);
 
 int getTagId(log_msg msg);
@@ -41,5 +47,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
-#endif  // STATS_UTIL_H
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index f3e6894..7648a91 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -83,7 +83,13 @@
 
   optional string stop_all = 4;
 
-  repeated KeyMatcher dimension = 5;
+  enum InitialValue {
+    UNKNOWN = 0;
+    FALSE = 1;
+  }
+  optional InitialValue initial_value = 5 [default = UNKNOWN];
+
+  repeated KeyMatcher dimension = 6;
 }
 
 message Condition {
@@ -106,56 +112,59 @@
 }
 
 message Alert {
-  message IncidentdDetails {
-    optional string alert_name = 1;
-    repeated int32 incidentd_sections = 2;
+    optional string name = 1;
+
+    optional string metric_name = 2;
+
+    message IncidentdDetails {
+        repeated int32 section = 1;
   }
-  optional IncidentdDetails incidentd_details = 1;
+  optional IncidentdDetails incidentd_details = 3;
 
-  optional int32 number_of_buckets = 3;
+  optional int32 number_of_buckets = 4;
 
-  optional int32 refractory_period_secs = 4;
+  optional int32 refractory_period_secs = 5;
 
-  optional int64 trigger_if_sum_gt = 5;
+  optional int64 trigger_if_sum_gt = 6;
+
+  optional int32 refractory_period_in_buckets = 7;
 }
 
 message EventMetric {
-  optional int64 metric_id = 1;
+    optional string name = 1;
 
-  optional string what = 2;
+    optional string what = 2;
 
-  optional string condition = 3;
+    optional string condition = 3;
 
-  repeated EventConditionLink links = 4;
+    repeated EventConditionLink links = 4;
 }
 
 message CountMetric {
-  optional int64 metric_id = 1;
+    optional string name = 1;
 
-  optional string what = 2;
+    optional string what = 2;
 
-  optional string condition = 3;
+    optional string condition = 3;
 
-  repeated KeyMatcher dimension = 4;
+    repeated KeyMatcher dimension = 4;
 
-  optional Bucket bucket = 5;
+    optional Bucket bucket = 5;
 
-  repeated Alert alerts = 6;
+    optional bool include_in_output = 6;
 
-  optional bool include_in_output = 7;
-
-  repeated EventConditionLink links = 8;
+    repeated EventConditionLink links = 7;
 }
 
 message DurationMetric {
-  optional int64 metric_id = 1;
+    optional string name = 1;
 
-  optional string what = 2;
+    optional string what = 2;
 
-  enum AggregationType {
-    DURATION_SUM = 1;
+    enum AggregationType {
+        DURATION_SUM = 1;
 
-    DURATION_MAX_SPARSE = 2;
+        DURATION_MAX_SPARSE = 2;
   }
   optional AggregationType type = 3;
 
@@ -165,51 +174,42 @@
 
   optional Bucket bucket = 6;
 
-  repeated Alert alerts = 7;
-
-  repeated EventConditionLink links = 8;
-
+  repeated EventConditionLink links = 7;
 }
 
 message GaugeMetric {
-  optional int64 metric_id = 1;
+    optional string name = 1;
 
-  optional string what = 2;
+    optional string what = 2;
 
-  optional int32 gauge_field = 3;
+    optional int32 gauge_field = 3;
 
-  optional string condition = 4;
+    optional string condition = 4;
 
-  repeated KeyMatcher dimension = 5;
+    repeated KeyMatcher dimension = 5;
 
-  optional Bucket bucket = 6;
+    optional Bucket bucket = 6;
 
-  repeated Alert alerts = 7;
-
-  repeated EventConditionLink links = 8;
+    repeated EventConditionLink links = 7;
 }
 
 message ValueMetric {
-  optional int64 metric_id = 1;
+    optional string name = 1;
 
-  optional string what = 2;
+    optional string what = 2;
 
-  optional int32 value_field = 3;
+    optional int32 value_field = 3;
 
-  optional string condition = 4;
+    optional string condition = 4;
 
-  repeated KeyMatcher dimension = 5;
+    repeated KeyMatcher dimension = 5;
 
-  optional Bucket bucket = 6;
+    optional Bucket bucket = 6;
 
-  repeated Alert alerts = 7;
+    repeated EventConditionLink links = 7;
 
-  repeated EventConditionLink links = 8;
-
-  enum Operation {
-    SUM = 1;
-  }
-  optional Operation operation = 9 [default = SUM];
+    enum Operation { SUM = 1; }
+    optional Operation operation = 9 [default = SUM];
 }
 
 message EventConditionLink {
@@ -220,19 +220,21 @@
 };
 
 message StatsdConfig {
-  optional int64 config_id = 1;
+    optional string name = 1;
 
-  repeated EventMetric event_metric = 2;
+    repeated EventMetric event_metric = 2;
 
-  repeated CountMetric count_metric = 3;
+    repeated CountMetric count_metric = 3;
 
-  repeated ValueMetric value_metric = 4;
+    repeated ValueMetric value_metric = 4;
 
-  repeated DurationMetric duration_metric = 5;
+    repeated GaugeMetric gauge_metric = 5;
 
-  repeated GaugeMetric gauge_metric = 6;
+    repeated DurationMetric duration_metric = 6;
 
-  repeated LogEntryMatcher log_entry_matcher = 7;
+    repeated LogEntryMatcher log_entry_matcher = 7;
 
-  repeated Condition condition = 8;
+    repeated Condition condition = 8;
+
+    repeated Alert alerts = 9;
 }
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index aa896ca..fad5de6 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -30,7 +30,7 @@
 namespace statsd {
 
 static ostream& operator<<(ostream& os, const StatsdConfig& config) {
-    return os << "StatsdConfig{id=" << config.config_id() << "}";
+    return os << "StatsdConfig{name=" << config.name().c_str() << "}";
 }
 
 }  // namespace statsd
@@ -56,8 +56,8 @@
 /**
  * Validate that the StatsdConfig is the one we wanted.
  */
-MATCHER_P(StatsdConfigEq, configId, "") {
-    return arg.config_id() == configId;
+MATCHER_P(StatsdConfigEq, name, "") {
+    return arg.name() == name;
 }
 
 /**
@@ -70,13 +70,13 @@
     manager->AddListener(listener);
 
     StatsdConfig config91;
-    config91.set_config_id(91);
+    config91.set_name("91");
     StatsdConfig config92;
-    config92.set_config_id(92);
+    config92.set_name("92");
     StatsdConfig config93;
-    config93.set_config_id(93);
+    config93.set_name("93");
     StatsdConfig config94;
-    config94.set_config_id(94);
+    config94.set_name("94");
 
     {
         InSequence s;
@@ -85,27 +85,27 @@
         // TODO: Remove this when we get rid of the fake one, and make this
         // test loading one from disk somewhere.
         EXPECT_CALL(*(listener.get()),
-                    OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq(12345)))
+                    OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq("12345")))
                 .RetiresOnSaturation();
         manager->Startup();
 
         // Add another one
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq(91)))
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("91")))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, "zzz"), config91);
 
         // Update It
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq(92)))
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("92")))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, "zzz"), config92);
 
         // Add one with the same uid but a different name
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq(93)))
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq("93")))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, "yyy"), config93);
 
         // Add one with the same name but a different uid
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq(94)))
+        EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq("94")))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(2, "zzz"), config94);
 
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 7333785..caa1cf4 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -41,7 +41,7 @@
 
 StatsdConfig buildGoodConfig() {
     StatsdConfig config;
-    config.set_config_id(12345L);
+    config.set_name("12345");
 
     LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
@@ -76,7 +76,7 @@
 
 StatsdConfig buildCircleMatchers() {
     StatsdConfig config;
-    config.set_config_id(12345L);
+    config.set_name("12345");
 
     LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
@@ -102,7 +102,7 @@
 
 StatsdConfig buildMissingMatchers() {
     StatsdConfig config;
-    config.set_config_id(12345L);
+    config.set_name("12345");
 
     LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
@@ -128,7 +128,7 @@
 
 StatsdConfig buildDimensionMetricsWithMultiTags() {
     StatsdConfig config;
-    config.set_config_id(12345L);
+    config.set_name("12345");
 
     LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
     eventMatcher->set_name("BATTERY_VERY_LOW");
@@ -150,7 +150,7 @@
 
     // Count process state changes, slice by uid, while SCREEN_IS_OFF
     CountMetric* metric = config.add_count_metric();
-    metric->set_metric_id(3);
+    metric->set_name("3");
     metric->set_what("BATTERY_LOW");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     KeyMatcher* keyMatcher = metric->add_dimension();
@@ -161,7 +161,7 @@
 
 StatsdConfig buildCircleConditions() {
     StatsdConfig config;
-    config.set_config_id(12345L);
+    config.set_name("12345");
 
     LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
     eventMatcher->set_name("SCREEN_IS_ON");
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 671f6d4..c64719e 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -13,7 +13,10 @@
 // limitations under the License.
 
 #include "packages/UidMap.h"
+#include "StatsLogProcessor.h"
 #include "config/ConfigKey.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
 
 #include <gtest/gtest.h>
 
@@ -29,6 +32,31 @@
 const string kApp1 = "app1.sharing.1";
 const string kApp2 = "app2.sharing.1";
 
+TEST(UidMapTest, TestIsolatedUID) {
+    sp<UidMap> m = new UidMap();
+    StatsLogProcessor p(m, nullptr);
+    LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
+    android_log_event_list* list = addEvent.GetAndroidLogEventList();
+    *list << 100;  // parent UID
+    *list << 101;  // isolated UID
+    *list << 1;    // Indicates creation.
+    addEvent.init();
+
+    EXPECT_EQ(101, m->getParentUidOrSelf(101));
+
+    p.OnLogEvent(addEvent);
+    EXPECT_EQ(100, m->getParentUidOrSelf(101));
+
+    LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1);
+    list = removeEvent.GetAndroidLogEventList();
+    *list << 100;  // parent UID
+    *list << 101;  // isolated UID
+    *list << 0;    // Indicates removal.
+    removeEvent.init();
+    p.OnLogEvent(removeEvent);
+    EXPECT_EQ(101, m->getParentUidOrSelf(101));
+}
+
 TEST(UidMapTest, TestMatching) {
     UidMap m;
     vector<int32_t> uids;
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
new file mode 100644
index 0000000..b8150d0
--- /dev/null
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -0,0 +1,239 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/anomaly/DiscreteAnomalyTracker.h"
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair_list,
+                      std::shared_ptr<DimToValMap> bucket) {
+    for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
+        (*bucket)[itr->first] += itr->second;
+    }
+}
+
+std::shared_ptr<DimToValMap> MockeBucket(
+        const std::vector<std::pair<string, long>>& key_value_pair_list) {
+    std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
+    AddValueToBucket(key_value_pair_list, bucket);
+    return bucket;
+}
+
+TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
+    Alert alert;
+    alert.set_number_of_buckets(3);
+    alert.set_refractory_period_in_buckets(3);
+    alert.set_trigger_if_sum_gt(2);
+
+    DiscreteAnomalyTracker anomaly_tracker(alert);
+
+    std::shared_ptr<DimToValMap> bucket0 = MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+    // Adds bucket #0
+    anomaly_tracker.addOrUpdateBucket(bucket0, 0);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+
+    // Adds bucket #0 again. The sum does not change.
+    anomaly_tracker.addOrUpdateBucket(bucket0, 0);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 0L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+
+    // Adds bucket #1.
+    std::shared_ptr<DimToValMap> bucket1 = MockeBucket({{"b", 2}});
+    anomaly_tracker.addOrUpdateBucket(bucket1, 1);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    // Alarm.
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+    // Adds bucket #1 again. The sum does not change.
+    anomaly_tracker.addOrUpdateBucket(bucket1, 1);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    // Alarm.
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+    // Adds bucket #2.
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 2);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 2L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    // Within refractory period.
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+    // Adds bucket #3.
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 3);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 3L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+
+    // Adds bucket #3.
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 2}}), 4);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 4L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    // Within refractory period.
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 5);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 5L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    // Within refractory period.
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 5L);
+}
+
+TEST(AnomalyTrackerTest, TestSparseBuckets) {
+    Alert alert;
+    alert.set_number_of_buckets(3);
+    alert.set_refractory_period_in_buckets(3);
+    alert.set_trigger_if_sum_gt(2);
+
+    DiscreteAnomalyTracker anomaly_tracker(alert);
+
+    // Add bucket #9
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}), 9);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 9L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+
+    // Add bucket #16
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 4}}), 16);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 16L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+    // Add bucket #18
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    // Within refractory period.
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+    // Add bucket #18 again.
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+    // Add bucket #20
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 3}, {"d", 1}}), 20);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 20L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1);
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+
+    // Add bucket #25
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"d", 1}}), 25);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 25L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1L);
+    EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+
+    // Add bucket #28
+    anomaly_tracker.addOrUpdateBucket(MockeBucket({{"e", 5}}), 28);
+    EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 28L);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+    EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("e")->second, 5L);
+    EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+    anomaly_tracker.declareAndDeclareAnomaly();
+    EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 3L);
+    EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 28L);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/ConditionTracker_test.cpp b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
similarity index 98%
rename from cmds/statsd/tests/ConditionTracker_test.cpp
rename to cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
index 2935ac7..23d6926 100644
--- a/cmds/statsd/tests/ConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
@@ -23,7 +23,6 @@
 using namespace android::os::statsd;
 using std::vector;
 
-
 #ifdef __ANDROID__
 TEST(ConditionTrackerTest, TestUnknownCondition) {
     LogicalOperation operation = LogicalOperation::AND;
@@ -39,7 +38,7 @@
     conditionResults.push_back(ConditionState::kTrue);
 
     EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
-            ConditionState::kUnknown);
+              ConditionState::kUnknown);
 }
 TEST(ConditionTrackerTest, TestAndCondition) {
     // Set up the matcher
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
new file mode 100644
index 0000000..05aad29
--- /dev/null
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -0,0 +1,469 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "src/condition/SimpleConditionTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using std::map;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
+                                         bool outputSlicedUid) {
+    SimpleCondition simpleCondition;
+    simpleCondition.set_start("WAKE_LOCK_ACQUIRE");
+    simpleCondition.set_stop("WAKE_LOCK_RELEASE");
+    simpleCondition.set_stop_all("RELEASE_ALL");
+    if (outputSlicedUid) {
+        KeyMatcher* keyMatcher = simpleCondition.add_dimension();
+        keyMatcher->set_key(1);
+    }
+
+    simpleCondition.set_count_nesting(countNesting);
+    simpleCondition.set_initial_value(defaultFalse ? SimpleCondition_InitialValue_FALSE
+                                                       : SimpleCondition_InitialValue_UNKNOWN);
+    return simpleCondition;
+}
+
+void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
+    auto list = event->GetAndroidLogEventList();
+    *list << uid;  // uid
+    *list << wl;
+    *list << acquire;
+    event->init();
+}
+
+map<string, HashableDimensionKey> getWakeLockQueryKey(int key, int uid,
+                                                      const string& conditionName) {
+    // test query
+    KeyValuePair kv1;
+    kv1.set_key(key);
+    kv1.set_value_int(uid);
+    vector<KeyValuePair> kv_list;
+    kv_list.push_back(kv1);
+    map<string, HashableDimensionKey> queryKey;
+    queryKey[conditionName] = getHashableKey(kv_list);
+    return queryKey;
+}
+
+TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
+    SimpleCondition simpleCondition;
+    simpleCondition.set_start("SCREEN_TURNED_ON");
+    simpleCondition.set_stop("SCREEN_TURNED_OFF");
+    simpleCondition.set_count_nesting(false);
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
+    trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+
+    SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // not matched start or stop. condition doesn't change
+    EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
+    EXPECT_FALSE(changedCache[0]);
+
+    // prepare a case for match start.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // now condition should change to true.
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+    EXPECT_TRUE(changedCache[0]);
+
+    // the case for match stop.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    // condition changes to false.
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    EXPECT_TRUE(changedCache[0]);
+
+    // match stop again.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // condition should still be false. not changed.
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    EXPECT_FALSE(changedCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
+    SimpleCondition simpleCondition;
+    simpleCondition.set_start("SCREEN_TURNED_ON");
+    simpleCondition.set_stop("SCREEN_TURNED_OFF");
+    simpleCondition.set_count_nesting(true);
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
+    trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+
+    SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*condition tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+
+    // one matched start
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+    EXPECT_TRUE(changedCache[0]);
+
+    // prepare for another matched start.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+    EXPECT_FALSE(changedCache[0]);
+
+    // ONE MATCHED STOP
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // result should still be true
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+    EXPECT_FALSE(changedCache[0]);
+
+    // ANOTHER MATCHED STOP
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // result should still be true
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    EXPECT_TRUE(changedCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
+    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+            true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
+    string conditionName = "WL_HELD_BY_UID2";
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+    trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
+    int uid = 111;
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event, uid, "wl1", 1);
+
+    // one matched start
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // Now test query
+    const auto queryKey = getWakeLockQueryKey(1, uid, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+    // another wake lock acquired by this uid
+    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event2, uid, "wl2", 1);
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_FALSE(changedCache[0]);
+
+    // wake lock 1 release
+    LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event3, uid, "wl1", 0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // nothing changes, because wake lock 2 is still held for this uid
+    EXPECT_FALSE(changedCache[0]);
+
+    LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event4, uid, "wl2", 0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // query again
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+            true /*nesting*/, true /*default to false*/, false /*slice output by uid*/);
+    string conditionName = "WL_HELD";
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+    trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
+    int uid1 = 111;
+    string uid1_wl1 = "wl1_1";
+    int uid2 = 222;
+    string uid2_wl1 = "wl2_1";
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event, uid1, uid1_wl1, 1);
+
+    // one matched start for uid1
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // Now test query
+    map<string, HashableDimensionKey> queryKey;
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+    // another wake lock acquired by this uid
+    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event2, uid2, uid2_wl1, 1);
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_FALSE(changedCache[0]);
+
+    // uid1 wake lock 1 release
+    LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event3, uid1, uid1_wl1, 0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // nothing changes, because uid2 is still holding wl.
+    EXPECT_FALSE(changedCache[0]);
+
+    LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event4, uid2, uid2_wl1, 0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // query again
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestStopAll) {
+    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+            true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
+    string conditionName = "WL_HELD_BY_UID3";
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+    trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
+    int uid1 = 111;
+    int uid2 = 222;
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event, uid1, "wl1", 1);
+
+    // one matched start
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // Now test query
+    const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+    // another wake lock acquired by uid2
+    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event2, uid2, "wl2", 1);
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // TEST QUERY
+    const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+
+    // stop all event
+    LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_TRUE(changedCache[0]);
+    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+
+    // TEST QUERY
+    const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+
+    // TEST QUERY
+    const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 5a4ee73..d07a84d 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -40,7 +40,7 @@
     int tagId = 1;
 
     CountMetric metric;
-    metric.set_metric_id(1);
+    metric.set_name("1");
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
 
     LogEvent event1(tagId, bucketStartTimeNs + 1);
@@ -92,7 +92,7 @@
     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     CountMetric metric;
-    metric.set_metric_id(1);
+    metric.set_name("1");
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
     metric.set_condition("SCREEN_ON");
 
@@ -129,7 +129,7 @@
     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     CountMetric metric;
-    metric.set_metric_id(1);
+    metric.set_name("1");
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
     metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
     EventConditionLink* link = metric.add_links();
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 76dbc73..0971d26 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -38,7 +38,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     EventMetric metric;
-    metric.set_metric_id(1);
+    metric.set_name("1");
 
     LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1);
     LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2);
@@ -61,7 +61,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     EventMetric metric;
-    metric.set_metric_id(1);
+    metric.set_name("1");
     metric.set_condition("SCREEN_ON");
 
     LogEvent event1(1, bucketStartTimeNs + 1);
@@ -87,7 +87,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     EventMetric metric;
-    metric.set_metric_id(1);
+    metric.set_name("1");
     metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
     EventConditionLink* link = metric.add_links();
     link->set_condition("APP_IN_BACKGROUND_PER_UID");
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
new file mode 100644
index 0000000..72b4194
--- /dev/null
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -0,0 +1,299 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "metrics_test_helper.h"
+#include "src/metrics/ValueMetricProducer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+using std::shared_ptr;
+using std::make_shared;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2*bucketSizeNs;
+
+    ValueMetric metric;
+    metric.set_name("1");
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_value_field(2);
+
+    int tagId = 1;
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    // TODO: pending refactor of StatsPullerManager
+    // For now we still need this so that it doesn't do real pulling.
+    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+
+    ValueMetricProducer valueProducer(metric, -1 /*-1 meaning no condition*/, wizard,tagId,
+                                      bucketStartTimeNs, pullerManager);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+    auto list = event->GetAndroidLogEventList();
+    *list << 1;
+    *list << 11;
+    event->init();
+    allData.push_back(event);
+
+    valueProducer.onDataPulled(allData);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // has one raw pair
+    EXPECT_EQ(1UL, curInterval.raw.size());
+    // value is 11, 11
+    EXPECT_EQ(11, curInterval.raw.front().first);
+    EXPECT_EQ(11, curInterval.raw.front().second);
+    ValueMetricProducer::Interval nextInterval = valueProducer.mNextSlicedBucket.begin()->second;
+    // has one raw pair
+    EXPECT_EQ(1UL, nextInterval.raw.size());
+    // value is 11, 0
+    EXPECT_EQ(11, nextInterval.raw.front().first);
+    EXPECT_EQ(0, nextInterval.raw.front().second);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    allData.clear();
+    event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    list = event->GetAndroidLogEventList();
+    *list << 1;
+    *list << 22;
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // has one raw pair
+    EXPECT_EQ(1UL, curInterval.raw.size());
+    // value is 22, 0
+    EXPECT_EQ(22, curInterval.raw.front().first);
+    EXPECT_EQ(0, curInterval.raw.front().second);
+    EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(11, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+    allData.clear();
+    event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
+    list = event->GetAndroidLogEventList();
+    *list << 1;
+    *list << 33;
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(1UL, curInterval.raw.size());
+    // value is 33, 0
+    EXPECT_EQ(33, curInterval.raw.front().first);
+    EXPECT_EQ(0, curInterval.raw.front().second);
+    EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(11, valueProducer.mPastBuckets.begin()->second.back().mValue);
+}
+
+/*
+ * Test pulled event with non sliced condition.
+ */
+TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+    ValueMetric metric;
+    metric.set_name("1");
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_value_field(2);
+    metric.set_condition("SCREEN_ON");
+
+    int tagId = 1;
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+        int64_t bucketStartTimeNs = 10000000000;
+        int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+        int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+        int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+        data->clear();
+        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+        auto list = event->GetAndroidLogEventList();
+        *list << 1;
+        *list << 100;
+        event->init();
+        data->push_back(event);
+        return true;
+    }))
+    .WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+        int64_t bucketStartTimeNs = 10000000000;
+        int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+        int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+        int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+        data->clear();
+        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+        auto list = event->GetAndroidLogEventList();
+        *list << 1;
+        *list << 120;
+        event->init();
+        data->push_back(event);
+        return true;
+    }));
+
+    ValueMetricProducer valueProducer(metric, 1, wizard,tagId,
+                                      bucketStartTimeNs, pullerManager);
+
+    valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // has one raw pair
+    EXPECT_EQ(1UL, curInterval.raw.size());
+    // value is 100, 0
+    EXPECT_EQ(100, curInterval.raw.front().first);
+    EXPECT_EQ(0, curInterval.raw.front().second);
+    EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    auto list = event->GetAndroidLogEventList();
+    *list << 1;
+    *list << 110;
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // has one raw pair
+    EXPECT_EQ(1UL, curInterval.raw.size());
+    // value is 110, 0
+    EXPECT_EQ(110, curInterval.raw.front().first);
+    EXPECT_EQ(0, curInterval.raw.front().second);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // has one raw pair
+    EXPECT_EQ(1UL, curInterval.raw.size());
+    // value is 110, 120
+    EXPECT_EQ(110, curInterval.raw.front().first);
+    EXPECT_EQ(120, curInterval.raw.front().second);
+}
+
+TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+    int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+    ValueMetric metric;
+    metric.set_name("1");
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_value_field(2);
+
+    int tagId = 1;
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+
+    ValueMetricProducer valueProducer(metric, -1, wizard,-1,
+                                      bucketStartTimeNs, pullerManager);
+
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+    auto list = event1->GetAndroidLogEventList();
+    *list << 1;
+    *list << 10;
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+    auto list2 = event2->GetAndroidLogEventList();
+    *list2 << 1;
+    *list2 << 20;
+    event2->init();
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1, false);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // has one raw pair
+    EXPECT_EQ(1UL, curInterval.raw.size());
+    // value is 10, 0
+    EXPECT_EQ(10, curInterval.raw.front().first);
+    EXPECT_EQ(0, curInterval.raw.front().second);
+    EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2, false);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // has one raw pair
+    EXPECT_EQ(2UL, curInterval.raw.size());
+    // value is 10, 20
+    EXPECT_EQ(10, curInterval.raw.front().first);
+    EXPECT_EQ(20, curInterval.raw.back().first);
+    EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+
+    valueProducer.flush_if_needed(bucket3StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 5fd7d62..fa221aa 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -14,6 +14,7 @@
 #pragma once
 
 #include "src/condition/ConditionWizard.h"
+#include "src/external/StatsPullerManager.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -30,6 +31,13 @@
                            const std::map<std::string, HashableDimensionKey>& conditionParameters));
 };
 
+class MockStatsPullerManager : public StatsPullerManager {
+public:
+    MOCK_METHOD3(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver, long intervalMs));
+    MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
+    MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
+};
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index 7703fdc..fb201ef 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -3943,9 +3943,6 @@
 android.telephony.VoLteServiceState
 android.telephony.VoLteServiceState$1
 android.telephony.gsm.GsmCellLocation
-android.telephony.ims.ImsServiceProxy$INotifyStatusChanged
-android.telephony.ims.ImsServiceProxyCompat
-android.telephony.ims.feature.IMMTelFeature
 android.telephony.ims.stub.ImsConfigImplBase
 android.telephony.ims.stub.ImsEcbmImplBase
 android.telephony.ims.stub.ImsUtImplBase
@@ -5208,6 +5205,8 @@
 com.android.internal.app.AlertController$ButtonHandler
 com.android.internal.app.AlertController$RecycleListView
 com.android.internal.app.AssistUtils
+com.android.internal.app.ColorDisplayController
+com.android.internal.app.ColorDisplayController$Callback
 com.android.internal.app.IAppOpsCallback
 com.android.internal.app.IAppOpsCallback$Stub
 com.android.internal.app.IAppOpsCallback$Stub$Proxy
@@ -5233,8 +5232,6 @@
 com.android.internal.app.IVoiceInteractionSessionShowCallback$Stub
 com.android.internal.app.IVoiceInteractor
 com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.NightDisplayController
-com.android.internal.app.NightDisplayController$Callback
 com.android.internal.app.ProcessMap
 com.android.internal.app.ResolverActivity
 com.android.internal.app.ToolbarActionBar
diff --git a/config/preloaded-classes b/config/preloaded-classes
index a9e90de..784c3f8 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2768,6 +2768,7 @@
 com.android.ims.ImsException
 com.android.internal.R$styleable
 com.android.internal.app.AlertController$AlertParams
+com.android.internal.app.ColorDisplayController
 com.android.internal.app.IAppOpsCallback
 com.android.internal.app.IAppOpsCallback$Stub
 com.android.internal.app.IAppOpsService
@@ -2780,7 +2781,6 @@
 com.android.internal.app.IVoiceInteractionManagerService$Stub
 com.android.internal.app.IVoiceInteractor
 com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.NightDisplayController
 com.android.internal.appwidget.IAppWidgetService
 com.android.internal.appwidget.IAppWidgetService$Stub
 com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 42b0f6b..1503445 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -2,3 +2,8 @@
     name: "IKeyAttestationApplicationIdProvider.aidl",
     srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
 }
+
+filegroup {
+    name: "IKeystoreService.aidl",
+    srcs: ["android/security/IKeystoreService.aidl"],
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a8863bf..99f3dee 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -193,10 +193,13 @@
  * <a name="Fragments"></a>
  * <h3>Fragments</h3>
  *
- * <p>Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}, Activity
- * implementations can make use of the {@link Fragment} class to better
+ * <p>The {@link android.support.v4.app.FragmentActivity} subclass
+ * can make use of the {@link android.support.v4.app.Fragment} class to better
  * modularize their code, build more sophisticated user interfaces for larger
- * screens, and help scale their application between small and large screens.
+ * screens, and help scale their application between small and large screens.</p>
+ *
+ * <p>For more information about using fragments, read the
+ * <a href="{@docRoot}guide/components/fragments.html">Fragments</a> developer guide.</p>
  *
  * <a name="ActivityLifecycle"></a>
  * <h3>Activity Lifecycle</h3>
@@ -915,7 +918,10 @@
 
     /**
      * Return the LoaderManager for this activity, creating it if needed.
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportLoaderManager()}
      */
+    @Deprecated
     public LoaderManager getLoaderManager() {
         return mFragments.getLoaderManager();
     }
@@ -2395,7 +2401,10 @@
     /**
      * Return the FragmentManager for interacting with fragments associated
      * with this activity.
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()}
      */
+    @Deprecated
     public FragmentManager getFragmentManager() {
         return mFragments.getFragmentManager();
     }
@@ -2404,7 +2413,11 @@
      * Called when a Fragment is being attached to this activity, immediately
      * after the call to its {@link Fragment#onAttach Fragment.onAttach()}
      * method and before {@link Fragment#onCreate Fragment.onCreate()}.
+     *
+     * @deprecated Use {@link
+     * android.support.v4.app.FragmentActivity#onAttachFragment(android.support.v4.app.Fragment)}
      */
+    @Deprecated
     public void onAttachFragment(Fragment fragment) {
     }
 
@@ -5106,7 +5119,11 @@
      *
      * @see Fragment#startActivity
      * @see Fragment#startActivityForResult
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment(
+     * android.support.v4.app.Fragment,Intent,int)}
      */
+    @Deprecated
     public void startActivityFromFragment(@NonNull Fragment fragment,
             @RequiresPermission Intent intent, int requestCode) {
         startActivityFromFragment(fragment, intent, requestCode, null);
@@ -5131,7 +5148,11 @@
      *
      * @see Fragment#startActivity
      * @see Fragment#startActivityForResult
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment(
+     * android.support.v4.app.Fragment,Intent,int,Bundle)}
      */
+    @Deprecated
     public void startActivityFromFragment(@NonNull Fragment fragment,
             @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
         startActivityForResult(fragment.mWho, intent, requestCode, options);
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index f99d1a8..0643414 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -19,7 +19,6 @@
 import android.os.FileUtils;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.system.ErrnoException;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -27,8 +26,6 @@
 import dalvik.system.BaseDexClassLoader;
 import dalvik.system.VMRuntime;
 
-import libcore.io.Libcore;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -155,23 +152,12 @@
             return;
         }
 
-        File realDexPath;
-        try {
-            // Secondary dex profiles are stored in the oat directory, next to the real dex file
-            // and have the same name with 'cur.prof' appended. We use the realpath because that
-            // is what installd is using when processing the dex file.
-            // NOTE: Keep in sync with installd.
-            realDexPath = new File(Libcore.os.realpath(dexPath));
-        } catch (ErrnoException ex) {
-            Slog.e(TAG, "Failed to get the real path of secondary dex " + dexPath
-                    + ":" + ex.getMessage());
-            // Do not continue with registration if we could not retrieve the real path.
-            return;
-        }
-
+        // Secondary dex profiles are stored in the oat directory, next to dex file
+        // and have the same name with 'cur.prof' appended.
         // NOTE: Keep this in sync with installd expectations.
-        File secondaryProfileDir = new File(realDexPath.getParent(), "oat");
-        File secondaryProfile = new File(secondaryProfileDir, realDexPath.getName() + ".cur.prof");
+        File dexPathFile = new File(dexPath);
+        File secondaryProfileDir = new File(dexPathFile.getParent(), "oat");
+        File secondaryProfile = new File(secondaryProfileDir, dexPathFile.getName() + ".cur.prof");
 
         // Create the profile if not already there.
         // Returns true if the file was created, false if the file already exists.
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index 7e0e4d8..a0fb6ee 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -136,7 +136,10 @@
  *
  * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
  *      embed}
+ *
+ * @deprecated Use {@link android.support.v4.app.DialogFragment}
  */
+@Deprecated
 public class DialogFragment extends Fragment
         implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
 
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 9377345..a92684b 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -256,7 +256,10 @@
  * <p>After each call to this function, a new entry is on the stack, and
  * pressing back will pop it to return the user to whatever previous state
  * the activity UI was in.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment}
  */
+@Deprecated
 public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {
     private static final ArrayMap<String, Class<?>> sClassMap =
             new ArrayMap<String, Class<?>>();
@@ -414,7 +417,10 @@
      * State information that has been retrieved from a fragment instance
      * through {@link FragmentManager#saveFragmentInstanceState(Fragment)
      * FragmentManager.saveFragmentInstanceState}.
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment.SavedState}
      */
+    @Deprecated
     public static class SavedState implements Parcelable {
         final Bundle mState;
 
@@ -458,7 +464,10 @@
     /**
      * Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when
      * there is an instantiation failure.
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment.InstantiationException}
      */
+    @Deprecated
     static public class InstantiationException extends AndroidRuntimeException {
         public InstantiationException(String msg, Exception cause) {
             super(msg, cause);
@@ -1031,7 +1040,10 @@
 
     /**
      * Return the LoaderManager for this fragment, creating it if needed.
+     *
+     * @deprecated Use {@link android.support.v4.app.Fragment#getLoaderManager()}
      */
+    @Deprecated
     public LoaderManager getLoaderManager() {
         if (mLoaderManager != null) {
             return mLoaderManager;
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
index d0aa0fd..e3e47ae 100644
--- a/core/java/android/app/FragmentBreadCrumbs.java
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -65,7 +65,10 @@
 
     /**
      * Interface to intercept clicks on the bread crumbs.
+     *
+     * @deprecated This widget is no longer supported.
      */
+    @Deprecated
     public interface OnBreadCrumbClickListener {
         /**
          * Called when a bread crumb is clicked.
diff --git a/core/java/android/app/FragmentContainer.java b/core/java/android/app/FragmentContainer.java
index f8836bc8..a1dd32f 100644
--- a/core/java/android/app/FragmentContainer.java
+++ b/core/java/android/app/FragmentContainer.java
@@ -24,7 +24,10 @@
 
 /**
  * Callbacks to a {@link Fragment}'s container.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentContainer}
  */
+@Deprecated
 public abstract class FragmentContainer {
     /**
      * Return the view with the given resource ID. May return {@code null} if the
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index cff94d8..cbb58d4 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -37,7 +37,10 @@
  * <p>
  * It is the responsibility of the host to take care of the Fragment's lifecycle.
  * The methods provided by {@link FragmentController} are for that purpose.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentController}
  */
+@Deprecated
 public class FragmentController {
     private final FragmentHostCallback<?> mHost;
 
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 5ef23e6..1edc68e 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -37,7 +37,10 @@
  * Fragments may be hosted by any object; such as an {@link Activity}. In order to
  * host fragments, implement {@link FragmentHostCallback}, overriding the methods
  * applicable to the host.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentHostCallback}
  */
+@Deprecated
 public abstract class FragmentHostCallback<E> extends FragmentContainer {
     private final Activity mActivity;
     final Context mContext;
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 0d5cd02..12e60b8 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -74,7 +74,10 @@
  * {@link android.support.v4.app.FragmentActivity}.  See the blog post
  * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
  * Fragments For All</a> for more details.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager}
  */
+@Deprecated
 public abstract class FragmentManager {
     /**
      * Representation of an entry on the fragment back stack, as created
@@ -86,7 +89,10 @@
      * <p>Note that you should never hold on to a BackStackEntry object;
      * the identifier as returned by {@link #getId} is the only thing that
      * will be persisted across activity instances.
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentManager.BackStackEntry}
      */
+    @Deprecated
     public interface BackStackEntry {
         /**
          * Return the unique identifier for the entry.  This is the only
@@ -129,7 +135,10 @@
 
     /**
      * Interface to watch for changes to the back stack.
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentManager.OnBackStackChangedListener}
      */
+    @Deprecated
     public interface OnBackStackChangedListener {
         /**
          * Called whenever the contents of the back stack change.
@@ -428,7 +437,10 @@
     /**
      * Callback interface for listening to fragment state changes that happen
      * within a given FragmentManager.
+     *
+     * @deprecated Use {@link android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks}
      */
+    @Deprecated
     public abstract static class FragmentLifecycleCallbacks {
         /**
          * Called right before the fragment's {@link Fragment#onAttach(Context)} method is called.
diff --git a/core/java/android/app/FragmentManagerNonConfig.java b/core/java/android/app/FragmentManagerNonConfig.java
index 50d3797..beb1a15 100644
--- a/core/java/android/app/FragmentManagerNonConfig.java
+++ b/core/java/android/app/FragmentManagerNonConfig.java
@@ -27,7 +27,10 @@
  * and passed to the state save and restore process for fragments in
  * {@link FragmentController#retainNonConfig()} and
  * {@link FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)}.</p>
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManagerNonConfig}
  */
+@Deprecated
 public class FragmentManagerNonConfig {
     private final List<Fragment> mFragments;
     private final List<FragmentManagerNonConfig> mChildNonConfigs;
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index c910e90..0f4a7fb 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -21,7 +21,10 @@
  * <a href="{@docRoot}guide/components/fragments.html">Fragments</a> developer
  * guide.</p>
  * </div>
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentTransaction}
  */
+@Deprecated
 public abstract class FragmentTransaction {
     /**
      * Calls {@link #add(int, Fragment, String)} with a 0 containerViewId.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e260967..d49e11f 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -418,22 +419,51 @@
      * different process.  In addition, if the given Intent resolves to
      * multiple activities, instead of displaying a dialog for the user to
      * select an activity, an exception will be thrown.
-     * 
+     *
      * <p>The function returns as soon as the activity goes idle following the
      * call to its {@link Activity#onCreate}.  Generally this means it has gone
      * through the full initialization including {@link Activity#onResume} and
      * drawn and displayed its initial window.
-     * 
+     *
      * @param intent Description of the activity to start.
-     * 
+     *
      * @see Context#startActivity
+     * @see #startActivitySync(Intent, Bundle)
      */
     public Activity startActivitySync(Intent intent) {
+        return startActivitySync(intent, null /* options */);
+    }
+
+    /**
+     * Start a new activity and wait for it to begin running before returning.
+     * In addition to being synchronous, this method as some semantic
+     * differences from the standard {@link Context#startActivity} call: the
+     * activity component is resolved before talking with the activity manager
+     * (its class name is specified in the Intent that this method ultimately
+     * starts), and it does not allow you to start activities that run in a
+     * different process.  In addition, if the given Intent resolves to
+     * multiple activities, instead of displaying a dialog for the user to
+     * select an activity, an exception will be thrown.
+     *
+     * <p>The function returns as soon as the activity goes idle following the
+     * call to its {@link Activity#onCreate}.  Generally this means it has gone
+     * through the full initialization including {@link Activity#onResume} and
+     * drawn and displayed its initial window.
+     *
+     * @param intent Description of the activity to start.
+     * @param options Additional options for how the Activity should be started.
+     * May be null if there are no options.  See {@link android.app.ActivityOptions}
+     * for how to build the Bundle supplied here; there are no supported definitions
+     * for building it manually.
+     *
+     * @see Context#startActivity(Intent, Bundle)
+     */
+    public Activity startActivitySync(Intent intent, @Nullable Bundle options) {
         validateNotAppThread();
 
         synchronized (mSync) {
             intent = new Intent(intent);
-    
+
             ActivityInfo ai = intent.resolveActivityInfo(
                 getTargetContext().getPackageManager(), 0);
             if (ai == null) {
@@ -447,7 +477,7 @@
                         + myProc + " resolved to different process "
                         + ai.processName + ": " + intent);
             }
-    
+
             intent.setComponent(new ComponentName(
                     ai.applicationInfo.packageName, ai.name));
             final ActivityWaiter aw = new ActivityWaiter(intent);
@@ -457,7 +487,7 @@
             }
             mWaitingActivities.add(aw);
 
-            getTargetContext().startActivity(intent);
+            getTargetContext().startActivity(intent, options);
 
             do {
                 try {
@@ -465,7 +495,7 @@
                 } catch (InterruptedException e) {
                 }
             } while (mWaitingActivities.contains(aw));
-         
+
             return aw.activity;
         }
     }
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
index 0b96d84..90b77b3 100644
--- a/core/java/android/app/ListFragment.java
+++ b/core/java/android/app/ListFragment.java
@@ -144,7 +144,10 @@
  *
  * @see #setListAdapter
  * @see android.widget.ListView
+ *
+ * @deprecated Use {@link android.support.v4.app.ListFragment}
  */
+@Deprecated
 public class ListFragment extends Fragment {
     final private Handler mHandler = new Handler();
 
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 56dfc58..7969684 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -54,11 +54,17 @@
  * <p>For more information about using loaders, read the
  * <a href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p>
  * </div>
+ *
+ * @deprecated Use {@link android.support.v4.app.LoaderManager}
  */
+@Deprecated
 public abstract class LoaderManager {
     /**
      * Callback interface for a client to interact with the manager.
+     *
+     * @deprecated Use {@link android.support.v4.app.LoaderManager.LoaderCallbacks}
      */
+    @Deprecated
     public interface LoaderCallbacks<D> {
         /**
          * Instantiate and return a new Loader for the given ID.
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6989db6..d813d66 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -41,6 +41,8 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutManager;
+import android.content.pm.crossprofile.CrossProfileApps;
+import android.content.pm.crossprofile.ICrossProfileApps;
 import android.content.res.Resources;
 import android.hardware.ConsumerIrManager;
 import android.hardware.ISerialManager;
@@ -81,6 +83,7 @@
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
+import android.net.NetworkWatchlistManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
 import android.net.nsd.INsdManager;
@@ -150,6 +153,7 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.app.ISoundTriggerService;
 import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.net.INetworkWatchlistManager;
 import com.android.internal.os.IDropBoxManagerService;
 import com.android.internal.policy.PhoneLayoutInflater;
 
@@ -862,6 +866,17 @@
                 return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
             }});
 
+        registerService(Context.NETWORK_WATCHLIST_SERVICE, NetworkWatchlistManager.class,
+                new CachedServiceFetcher<NetworkWatchlistManager>() {
+                    @Override
+                    public NetworkWatchlistManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b =
+                                ServiceManager.getServiceOrThrow(Context.NETWORK_WATCHLIST_SERVICE);
+                        return new NetworkWatchlistManager(ctx,
+                                INetworkWatchlistManager.Stub.asInterface(b));
+                    }});
+
         registerService(Context.SYSTEM_HEALTH_SERVICE, SystemHealthManager.class,
                 new CachedServiceFetcher<SystemHealthManager>() {
             @Override
@@ -909,6 +924,18 @@
             public RulesManager createService(ContextImpl ctx) {
                 return new RulesManager(ctx.getOuterContext());
             }});
+
+        registerService(Context.CROSS_PROFILE_APPS_SERVICE, CrossProfileApps.class,
+                new CachedServiceFetcher<CrossProfileApps>() {
+                    @Override
+                    public CrossProfileApps createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(
+                                Context.CROSS_PROFILE_APPS_SERVICE);
+                        return new CrossProfileApps(ctx.getOuterContext(),
+                                ICrossProfileApps.Stub.asInterface(b));
+                    }
+                });
     }
 
     /**
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index de27b4f..2c1fad1 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -500,15 +500,12 @@
      * @hide
      */
     public boolean supportSplitScreenWindowingMode() {
-        return supportSplitScreenWindowingMode(mWindowingMode, mActivityType);
+        return supportSplitScreenWindowingMode(mActivityType);
     }
 
     /** @hide */
-    public static boolean supportSplitScreenWindowingMode(int windowingMode, int activityType) {
-        if (activityType == ACTIVITY_TYPE_ASSISTANT) {
-            return false;
-        }
-        return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
+    public static boolean supportSplitScreenWindowingMode(int activityType) {
+        return activityType != ACTIVITY_TYPE_ASSISTANT;
     }
 
     /** @hide */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a2e5cc9..f0226b7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6097,8 +6097,8 @@
 
     /**
      * Flag used by {@link #createAndManageUser} to specify that the user should be created
-     * ephemeral.
-     * @hide
+     * ephemeral. Ephemeral users will be removed after switching to another user or rebooting the
+     * device.
      */
     public static final int MAKE_USER_EPHEMERAL = 0x0002;
 
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index b640bd5..530d84b 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -16,6 +16,12 @@
 
 package android.app.job;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.util.TimeUtils.formatDuration;
 
 import android.annotation.BytesLong;
@@ -25,6 +31,8 @@
 import android.annotation.RequiresPermission;
 import android.content.ClipData;
 import android.content.ComponentName;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
 import android.net.Uri;
 import android.os.BaseBundle;
 import android.os.Bundle;
@@ -56,6 +64,7 @@
             NETWORK_TYPE_ANY,
             NETWORK_TYPE_UNMETERED,
             NETWORK_TYPE_NOT_ROAMING,
+            NETWORK_TYPE_CELLULAR,
             NETWORK_TYPE_METERED,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -69,8 +78,21 @@
     public static final int NETWORK_TYPE_UNMETERED = 2;
     /** This job requires network connectivity that is not roaming. */
     public static final int NETWORK_TYPE_NOT_ROAMING = 3;
-    /** This job requires metered connectivity such as most cellular data networks. */
-    public static final int NETWORK_TYPE_METERED = 4;
+    /** This job requires network connectivity that is a cellular network. */
+    public static final int NETWORK_TYPE_CELLULAR = 4;
+
+    /**
+     * This job requires metered connectivity such as most cellular data
+     * networks.
+     *
+     * @deprecated Cellular networks may be unmetered, or Wi-Fi networks may be
+     *             metered, so this isn't a good way of selecting a specific
+     *             transport. Instead, use {@link #NETWORK_TYPE_CELLULAR} or
+     *             {@link android.net.NetworkRequest.Builder#addTransportType(int)}
+     *             if your job requires a specific network transport.
+     */
+    @Deprecated
+    public static final int NETWORK_TYPE_METERED = NETWORK_TYPE_CELLULAR;
 
     /** Sentinel value indicating that bytes are unknown. */
     public static final int NETWORK_BYTES_UNKNOWN = -1;
@@ -253,7 +275,7 @@
     private final long triggerContentMaxDelay;
     private final boolean hasEarlyConstraint;
     private final boolean hasLateConstraint;
-    private final int networkType;
+    private final NetworkRequest networkRequest;
     private final long networkBytes;
     private final long minLatencyMillis;
     private final long maxExecutionDelayMillis;
@@ -385,10 +407,37 @@
     }
 
     /**
-     * The kind of connectivity requirements that the job has.
+     * Return the basic description of the kind of network this job requires.
+     *
+     * @deprecated This method attempts to map {@link #getRequiredNetwork()}
+     *             into the set of simple constants, which results in a loss of
+     *             fidelity. Callers should move to using
+     *             {@link #getRequiredNetwork()} directly.
+     * @see Builder#setRequiredNetworkType(int)
      */
+    @Deprecated
     public @NetworkType int getNetworkType() {
-        return networkType;
+        if (networkRequest == null) {
+            return NETWORK_TYPE_NONE;
+        } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+            return NETWORK_TYPE_UNMETERED;
+        } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+            return NETWORK_TYPE_NOT_ROAMING;
+        } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+            return NETWORK_TYPE_CELLULAR;
+        } else {
+            return NETWORK_TYPE_ANY;
+        }
+    }
+
+    /**
+     * Return the detailed description of the kind of network this job requires,
+     * or {@code null} if no specific kind of network is required.
+     *
+     * @see Builder#setRequiredNetwork(NetworkRequest)
+     */
+    public @Nullable NetworkRequest getRequiredNetwork() {
+        return networkRequest;
     }
 
     /**
@@ -438,8 +487,7 @@
      * job does not recur periodically.
      */
     public long getIntervalMillis() {
-        final long minInterval = getMinPeriodMillis();
-        return intervalMillis >= minInterval ? intervalMillis : minInterval;
+        return intervalMillis;
     }
 
     /**
@@ -447,10 +495,7 @@
      * execute at any time in a window of flex length at the end of the period.
      */
     public long getFlexMillis() {
-        long interval = getIntervalMillis();
-        long percentClamp = 5 * interval / 100;
-        long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, getMinFlexMillis()));
-        return clampedFlex <= interval ? clampedFlex : interval;
+        return flexMillis;
     }
 
     /**
@@ -459,8 +504,7 @@
      * to 30 seconds, minimum is currently 10 seconds.
      */
     public long getInitialBackoffMillis() {
-        final long minBackoff = getMinBackoffMillis();
-        return initialBackoffMillis >= minBackoff ? initialBackoffMillis : minBackoff;
+        return initialBackoffMillis;
     }
 
     /**
@@ -538,7 +582,7 @@
         if (hasLateConstraint != j.hasLateConstraint) {
             return false;
         }
-        if (networkType != j.networkType) {
+        if (!Objects.equals(networkRequest, j.networkRequest)) {
             return false;
         }
         if (networkBytes != j.networkBytes) {
@@ -601,7 +645,9 @@
         hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay);
         hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint);
         hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint);
-        hashCode = 31 * hashCode + networkType;
+        if (networkRequest != null) {
+            hashCode = 31 * hashCode + networkRequest.hashCode();
+        }
         hashCode = 31 * hashCode + Long.hashCode(networkBytes);
         hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
         hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
@@ -632,7 +678,11 @@
         triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
         triggerContentUpdateDelay = in.readLong();
         triggerContentMaxDelay = in.readLong();
-        networkType = in.readInt();
+        if (in.readInt() != 0) {
+            networkRequest = NetworkRequest.CREATOR.createFromParcel(in);
+        } else {
+            networkRequest = null;
+        }
         networkBytes = in.readLong();
         minLatencyMillis = in.readLong();
         maxExecutionDelayMillis = in.readLong();
@@ -661,7 +711,7 @@
                 : null;
         triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
         triggerContentMaxDelay = b.mTriggerContentMaxDelay;
-        networkType = b.mNetworkType;
+        networkRequest = b.mNetworkRequest;
         networkBytes = b.mNetworkBytes;
         minLatencyMillis = b.mMinLatencyMillis;
         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
@@ -699,7 +749,12 @@
         out.writeTypedArray(triggerContentUris, flags);
         out.writeLong(triggerContentUpdateDelay);
         out.writeLong(triggerContentMaxDelay);
-        out.writeInt(networkType);
+        if (networkRequest != null) {
+            out.writeInt(1);
+            networkRequest.writeToParcel(out, flags);
+        } else {
+            out.writeInt(0);
+        }
         out.writeLong(networkBytes);
         out.writeLong(minLatencyMillis);
         out.writeLong(maxExecutionDelayMillis);
@@ -833,7 +888,7 @@
         private int mFlags;
         // Requirements.
         private int mConstraintFlags;
-        private int mNetworkType;
+        private NetworkRequest mNetworkRequest;
         private long mNetworkBytes = NETWORK_BYTES_UNKNOWN;
         private ArrayList<TriggerContentUri> mTriggerContentUris;
         private long mTriggerContentUpdateDelay = -1;
@@ -934,24 +989,84 @@
         }
 
         /**
-         * Set some description of the kind of network type your job needs to
-         * have. Not calling this function means the network is not necessary,
-         * as the default is {@link #NETWORK_TYPE_NONE}. Bear in mind that
-         * calling this function defines network as a strict requirement for
-         * your job. If the network requested is not available your job will
-         * never run. See {@link #setOverrideDeadline(long)} to change this
-         * behaviour.
+         * Set basic description of the kind of network your job requires. If
+         * you need more precise control over network capabilities, see
+         * {@link #setRequiredNetwork(NetworkRequest)}.
+         * <p>
+         * If your job doesn't need a network connection, you don't need to call
+         * this method, as the default value is {@link #NETWORK_TYPE_NONE}.
+         * <p>
+         * Calling this method defines network as a strict requirement for your
+         * job. If the network requested is not available your job will never
+         * run. See {@link #setOverrideDeadline(long)} to change this behavior.
+         * Calling this method will override any requirements previously defined
+         * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only
+         * want to call one of these methods.
          * <p class="note">
-         * Note: When your job executes in
+         * When your job executes in
          * {@link JobService#onStartJob(JobParameters)}, be sure to use the
          * specific network returned by {@link JobParameters#getNetwork()},
          * otherwise you'll use the default network which may not meet this
          * constraint.
          *
+         * @see #setRequiredNetwork(NetworkRequest)
+         * @see JobInfo#getNetworkType()
          * @see JobParameters#getNetwork()
          */
         public Builder setRequiredNetworkType(@NetworkType int networkType) {
-            mNetworkType = networkType;
+            if (networkType == NETWORK_TYPE_NONE) {
+                return setRequiredNetwork(null);
+            } else {
+                final NetworkRequest.Builder builder = new NetworkRequest.Builder();
+
+                // All types require validated Internet
+                builder.addCapability(NET_CAPABILITY_INTERNET);
+                builder.addCapability(NET_CAPABILITY_VALIDATED);
+                builder.removeCapability(NET_CAPABILITY_NOT_VPN);
+
+                if (networkType == NETWORK_TYPE_ANY) {
+                    // No other capabilities
+                } else if (networkType == NETWORK_TYPE_UNMETERED) {
+                    builder.addCapability(NET_CAPABILITY_NOT_METERED);
+                } else if (networkType == NETWORK_TYPE_NOT_ROAMING) {
+                    builder.addCapability(NET_CAPABILITY_NOT_ROAMING);
+                } else if (networkType == NETWORK_TYPE_CELLULAR) {
+                    builder.addTransportType(TRANSPORT_CELLULAR);
+                }
+
+                return setRequiredNetwork(builder.build());
+            }
+        }
+
+        /**
+         * Set detailed description of the kind of network your job requires.
+         * <p>
+         * If your job doesn't need a network connection, you don't need to call
+         * this method, as the default is {@code null}.
+         * <p>
+         * Calling this method defines network as a strict requirement for your
+         * job. If the network requested is not available your job will never
+         * run. See {@link #setOverrideDeadline(long)} to change this behavior.
+         * Calling this method will override any requirements previously defined
+         * by {@link #setRequiredNetworkType(int)}; you typically only want to
+         * call one of these methods.
+         * <p class="note">
+         * When your job executes in
+         * {@link JobService#onStartJob(JobParameters)}, be sure to use the
+         * specific network returned by {@link JobParameters#getNetwork()},
+         * otherwise you'll use the default network which may not meet this
+         * constraint.
+         *
+         * @param networkRequest The detailed description of the kind of network
+         *            this job requires, or {@code null} if no specific kind of
+         *            network is required. Defining a {@link NetworkSpecifier}
+         *            is only supported for jobs that aren't persisted.
+         * @see #setRequiredNetworkType(int)
+         * @see JobInfo#getRequiredNetwork()
+         * @see JobParameters#getNetwork()
+         */
+        public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) {
+            mNetworkRequest = networkRequest;
             return this;
         }
 
@@ -1140,6 +1255,21 @@
          *                   higher.
          */
         public Builder setPeriodic(long intervalMillis, long flexMillis) {
+            final long minPeriod = getMinPeriodMillis();
+            if (intervalMillis < minPeriod) {
+                Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job "
+                        + mJobId + " is too small; raising to " + formatDuration(minPeriod));
+                intervalMillis = minPeriod;
+            }
+
+            final long percentClamp = 5 * intervalMillis / 100;
+            final long minFlex = Math.max(percentClamp, getMinFlexMillis());
+            if (flexMillis < minFlex) {
+                Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId
+                        + " is too small; raising to " + formatDuration(minFlex));
+                flexMillis = minFlex;
+            }
+
             mIsPeriodic = true;
             mIntervalMillis = intervalMillis;
             mFlexMillis = flexMillis;
@@ -1189,6 +1319,13 @@
          */
         public Builder setBackoffCriteria(long initialBackoffMillis,
                 @BackoffPolicy int backoffPolicy) {
+            final long minBackoff = getMinBackoffMillis();
+            if (initialBackoffMillis < minBackoff) {
+                Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job "
+                        + mJobId + " is too small; raising to " + formatDuration(minBackoff));
+                initialBackoffMillis = minBackoff;
+            }
+
             mBackoffPolicySet = true;
             mInitialBackoffMillis = initialBackoffMillis;
             mBackoffPolicy = backoffPolicy;
@@ -1213,16 +1350,22 @@
         public JobInfo build() {
             // Allow jobs with no constraints - What am I, a database?
             if (!mHasEarlyConstraint && !mHasLateConstraint && mConstraintFlags == 0 &&
-                    mNetworkType == NETWORK_TYPE_NONE &&
+                    mNetworkRequest == null &&
                     mTriggerContentUris == null) {
                 throw new IllegalArgumentException("You're trying to build a job with no " +
                         "constraints, this is not allowed.");
             }
             // Check that network estimates require network type
-            if (mNetworkBytes > 0 && mNetworkType == NETWORK_TYPE_NONE) {
+            if (mNetworkBytes > 0 && mNetworkRequest == null) {
                 throw new IllegalArgumentException(
                         "Can't provide estimated network usage without requiring a network");
             }
+            // We can't serialize network specifiers
+            if (mIsPersisted && mNetworkRequest != null
+                    && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) {
+                throw new IllegalArgumentException(
+                        "Network specifiers aren't supported for persistent jobs");
+            }
             // Check that a deadline was not set on a periodic job.
             if (mIsPeriodic) {
                 if (mMaxExecutionDelayMillis != 0L) {
@@ -1257,31 +1400,7 @@
                         " back-off policy, so calling setBackoffCriteria with" +
                         " setRequiresDeviceIdle is an error.");
             }
-            JobInfo job = new JobInfo(this);
-            if (job.isPeriodic()) {
-                if (job.intervalMillis != job.getIntervalMillis()) {
-                    StringBuilder builder = new StringBuilder();
-                    builder.append("Specified interval for ")
-                            .append(String.valueOf(mJobId))
-                            .append(" is ");
-                    formatDuration(mIntervalMillis, builder);
-                    builder.append(". Clamped to ");
-                    formatDuration(job.getIntervalMillis(), builder);
-                    Log.w(TAG, builder.toString());
-                }
-                if (job.flexMillis != job.getFlexMillis()) {
-                    StringBuilder builder = new StringBuilder();
-                    builder.append("Specified flex for ")
-                            .append(String.valueOf(mJobId))
-                            .append(" is ");
-                    formatDuration(mFlexMillis, builder);
-                    builder.append(". Clamped to ");
-                    formatDuration(job.getFlexMillis(), builder);
-                    Log.w(TAG, builder.toString());
-                }
-            }
-            return job;
+            return new JobInfo(this);
         }
     }
-
 }
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index dbaace2..29e7439 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -118,7 +118,15 @@
             AppIdleStateChangeListener listener);
 
     public static abstract class AppIdleStateChangeListener {
-        public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle);
+
+        /** Callback to inform listeners that the idle state has changed to a new bucket. */
+        public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle,
+                                                   int bucket);
+
+        /**
+         * Callback to inform listeners that the parole state has changed. This means apps are
+         * allowed to do work even if they're idle or in a low bucket.
+         */
         public abstract void onParoleStateChanged(boolean isParoleOn);
     }
 
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 4035ee1..0569913 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -375,7 +375,7 @@
             IBluetooth bluetoothProxy =
                     BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
             if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
-            mPfd = bluetoothProxy.connectSocket(mDevice, mType,
+            mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
                     mUuid, mPort, getSecurityFlags());
             synchronized (this) {
                 if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
@@ -417,7 +417,7 @@
             return -1;
         }
         try {
-            mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName,
+            mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
                     mUuid, mPort, getSecurityFlags());
         } catch (RemoteException e) {
             Log.e(TAG, Log.getStackTraceString(new Throwable()));
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
index b7545bf..6e9f09c 100644
--- a/core/java/android/content/AsyncTaskLoader.java
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -49,7 +49,10 @@
  *      fragment}
  *
  * @param <D> the data type to be loaded.
+ *
+ * @deprecated Use {@link android.support.v4.content.AsyncTaskLoader}
  */
+@Deprecated
 public abstract class AsyncTaskLoader<D> extends Loader<D> {
     static final String TAG = "AsyncTaskLoader";
     static final boolean DEBUG = false;
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index f8c139f..2d490a0 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -22,6 +22,7 @@
 import android.database.CrossProcessCursorWrapper;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.DeadObjectException;
@@ -102,8 +103,16 @@
                 if (sAnrHandler == null) {
                     sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
                 }
+
+                // If the remote process hangs, we're going to kill it, so we're
+                // technically okay doing blocking calls.
+                Binder.allowBlocking(mContentProvider.asBinder());
             } else {
                 mAnrRunnable = null;
+
+                // If we're no longer watching for hangs, revert back to default
+                // blocking behavior.
+                Binder.defaultBlocking(mContentProvider.asBinder());
             }
         }
     }
@@ -511,6 +520,10 @@
     private boolean closeInternal() {
         mCloseGuard.close();
         if (mClosed.compareAndSet(false, true)) {
+            // We can't do ANR checks after we cease to exist! Reset any
+            // blocking behavior changes we might have made.
+            setDetectNotResponding(0);
+
             if (mStable) {
                 return mContentResolver.releaseProvider(mContentProvider);
             } else {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c165fb3..72f75112 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3413,6 +3413,8 @@
     public static final String NETWORK_STATS_SERVICE = "netstats";
     /** {@hide} */
     public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+    /** {@hide} */
+    public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
@@ -4071,6 +4073,14 @@
     public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.content.pm.crossprofile.CrossProfileApps} for cross profile operations.
+     *
+     * @see #getSystemService
+     */
+    public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index c78871c..33386e5 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -38,7 +38,10 @@
  * in the desired paramters with {@link #setUri(Uri)}, {@link #setSelection(String)},
  * {@link #setSelectionArgs(String[])}, {@link #setSortOrder(String)},
  * and {@link #setProjection(String[])}.
+ *
+ * @deprecated Use {@link android.support.v4.content.CursorLoader}
  */
+@Deprecated
 public class CursorLoader extends AsyncTaskLoader<Cursor> {
     final ForceLoadContentObserver mObserver;
 
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 3faf13b..80f9a14 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -48,7 +48,10 @@
  * </div>
  *
  * @param <D> The result returned when the load is complete
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader}
  */
+@Deprecated
 public class Loader<D> {
     int mId;
     OnLoadCompleteListener<D> mListener;
@@ -66,7 +69,10 @@
      * is told it has changed.  You do not normally need to use this yourself;
      * it is used for you by {@link CursorLoader} to take care of executing
      * an update when the cursor's backing data changes.
+     *
+     * @deprecated Use {@link android.support.v4.content.Loader.ForceLoadContentObserver}
      */
+    @Deprecated
     public final class ForceLoadContentObserver extends ContentObserver {
         public ForceLoadContentObserver() {
             super(new Handler());
@@ -90,7 +96,10 @@
      * to find out when a Loader it is managing has completed so that this can
      * be reported to its client.  This interface should only be used if a
      * Loader is not being used in conjunction with LoaderManager.
+     *
+     * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCompleteListener}
      */
+    @Deprecated
     public interface OnLoadCompleteListener<D> {
         /**
          * Called on the thread that created the Loader when the load is complete.
@@ -108,7 +117,10 @@
      * to find out when a Loader it is managing has been canceled so that it
      * can schedule the next Loader.  This interface should only be used if a
      * Loader is not being used in conjunction with LoaderManager.
+     *
+     * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCanceledListener}
      */
+    @Deprecated
     public interface OnLoadCanceledListener<D> {
         /**
          * Called on the thread that created the Loader when the load is canceled.
diff --git a/core/java/android/content/pm/crossprofile/CrossProfileApps.java b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
new file mode 100644
index 0000000..c441b5f
--- /dev/null
+++ b/core/java/android/content/pm/crossprofile/CrossProfileApps.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.content.pm.crossprofile;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.List;
+
+/**
+ * Class for handling cross profile operations. Apps can use this class to interact with its
+ * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can
+ * use this class to start its main activity in managed profile.
+ */
+public class CrossProfileApps {
+    private final Context mContext;
+    private final ICrossProfileApps mService;
+
+    /** @hide */
+    public CrossProfileApps(Context context, ICrossProfileApps service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Starts the specified main activity of the caller package in the specified profile.
+     *
+     * @param component The ComponentName of the activity to launch, it must be exported and has
+     *        action {@link android.content.Intent#ACTION_MAIN}, category
+     *        {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
+     *        be thrown.
+     * @param user The UserHandle of the profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon, see
+     *                     {@link android.content.Intent#setSourceBounds(Rect)}.
+     * @param startActivityOptions Options to pass to startActivity
+     */
+    public void startMainActivity(@NonNull ComponentName component, @NonNull UserHandle user,
+            @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
+        try {
+            mService.startActivityAsUser(mContext.getPackageName(),
+                    component, sourceBounds, startActivityOptions, user);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return a list of user profiles that that the caller can use when calling other APIs in this
+     * class.
+     * <p>
+     * A user profile would be considered as a valid target user profile, provided that:
+     * <ul>
+     * <li>It gets caller app installed</li>
+     * <li>It is not equal to the calling user</li>
+     * <li>It is in the same profile group of calling user profile</li>
+     * <li>It is enabled</li>
+     * </ul>
+     *
+     * @see UserManager#getUserProfiles()
+     */
+    public @NonNull List<UserHandle> getTargetUserProfiles() {
+        try {
+            return mService.getTargetUserProfiles(mContext.getPackageName());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl b/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
new file mode 100644
index 0000000..dd8d04f
--- /dev/null
+++ b/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.crossprofile;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * @hide
+ */
+interface ICrossProfileApps {
+    void startActivityAsUser(in String callingPackage, in ComponentName component, in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
+    List<UserHandle> getTargetUserProfiles(in String callingPackage);
+}
\ No newline at end of file
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index dfd3bbf..26efda1 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2105,6 +2105,7 @@
                 break;
             case DENSITY_DPI_NONE:
                 parts.add("nodpi");
+                break;
             default:
                 parts.add(config.densityDpi + "dpi");
                 break;
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 361b81b..b692039 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -416,7 +416,8 @@
         boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
                 != mConfiguration.foreignKeyConstraintsEnabled;
         boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
-                & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
+                & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0
+                || configuration.useCompatibilityWal != mConfiguration.useCompatibilityWal;
         boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
 
         // Update configuration parameters.
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index bb2a517..d6d9764 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -83,7 +83,6 @@
     /**
      * Returns true if compatibility WAL mode is supported. In this mode, only
      * database journal mode is changed. Connection pool will use at most one connection.
-     * @hide
      */
     public static boolean isCompatibilityWalSupported() {
         return SystemProperties.getBoolean("debug.sqlite.compatibility_wal_supported",
diff --git a/core/java/android/database/sqlite/package.html b/core/java/android/database/sqlite/package.html
index 864a9bb..4d6ba28 100644
--- a/core/java/android/database/sqlite/package.html
+++ b/core/java/android/database/sqlite/package.html
@@ -20,6 +20,8 @@
 <p>The version of SQLite depends on the version of Android. See the following table:
 <table style="width:auto;">
   <tr><th>Android API</th><th>SQLite Version</th></tr>
+  <tr><td>API 27</td><td>3.19</td></tr>
+  <tr><td>API 26</td><td>3.18</td></tr>
   <tr><td>API 24</td><td>3.9</td></tr>
   <tr><td>API 21</td><td>3.8</td></tr>
   <tr><td>API 11</td><td>3.7</td></tr>
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index da771e4..ff69bd8 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -249,7 +249,7 @@
      * <p>This function can also be called in case where multiple surfaces share the same
      * OutputConfiguration, and one of the surfaces becomes available after the {@link
      * CameraCaptureSession} is created. In that case, the application must first create the
-     * OutputConfiguration with the available Surface, then enable furture surface sharing via
+     * OutputConfiguration with the available Surface, then enable further surface sharing via
      * {@link OutputConfiguration#enableSurfaceSharing}, before creating the CameraCaptureSession.
      * After the CameraCaptureSession is created, and once the extra Surface becomes available, the
      * application must then call {@link OutputConfiguration#addSurface} before finalizing the
@@ -645,6 +645,44 @@
     public abstract Surface getInputSurface();
 
     /**
+     * Update {@link OutputConfiguration} after configuration finalization see
+     * {@link #finalizeOutputConfigurations}.
+     *
+     * <p>Any {@link OutputConfiguration} that has been modified via calls to
+     * {@link OutputConfiguration#addSurface} or {@link OutputConfiguration#removeSurface} must be
+     * updated. After the update call returns without throwing exceptions any newly added surfaces
+     * can be referenced in subsequent capture requests.</p>
+     *
+     * <p>Surfaces that get removed must not be part of any active repeating or single/burst
+     * request or have any pending results. Consider updating any repeating requests first via
+     * {@link #setRepeatingRequest} or {@link #setRepeatingBurst} and then wait for the last frame
+     * number when the sequence completes {@link CaptureCallback#onCaptureSequenceCompleted}
+     * before calling updateOutputConfiguration to remove a previously active Surface.</p>
+     *
+     * <p>Surfaces that get added must not be part of any other registered
+     * {@link OutputConfiguration}.</p>
+     *
+     * @param config Modified output configuration.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error.
+     * @throws IllegalArgumentException if an attempt was made to add a {@link Surface} already
+     *                               in use by another buffer-producing API, such as MediaCodec or
+     *                               a different camera device or {@link OutputConfiguration}; or
+     *                               new surfaces are not compatible (see
+     *                               {@link OutputConfiguration#enableSurfaceSharing}); or a
+     *                               {@link Surface} that was removed from the modified
+     *                               {@link OutputConfiguration} still has pending requests.
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     */
+    public void updateOutputConfiguration(OutputConfiguration config)
+        throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * Close this capture session asynchronously.
      *
      * <p>Closing a session frees up the target output Surfaces of the session for reuse with either
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index c7654c9..374789c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -314,6 +314,20 @@
     }
 
     @Override
+    public void updateOutputConfiguration(OutputConfiguration config)
+            throws CameraAccessException {
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            if (DEBUG) {
+                Log.v(TAG, mIdString + "updateOutputConfiguration");
+            }
+
+            mDeviceImpl.updateOutputConfiguration(config);
+        }
+    }
+
+    @Override
     public boolean isReprocessable() {
         return mInput != null;
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index fec7fd9..8c4dbfa 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -235,6 +235,13 @@
     }
 
     @Override
+    public void updateOutputConfiguration(OutputConfiguration config)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+                + " this method");
+    }
+
+    @Override
     public void close() {
         mSessionImpl.close();
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index bfeb14d..6787d84 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -764,6 +764,24 @@
         }
     }
 
+    public void updateOutputConfiguration(OutputConfiguration config)
+            throws CameraAccessException {
+        synchronized(mInterfaceLock) {
+            int streamId = -1;
+            for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+                if (config.getSurface() == mConfiguredOutputs.valueAt(i).getSurface()) {
+                    streamId = mConfiguredOutputs.keyAt(i);
+                    break;
+                }
+            }
+            if (streamId == -1) {
+                throw new IllegalArgumentException("Invalid output configuration");
+            }
+
+            mRemoteDevice.updateOutputConfiguration(streamId, config);
+        }
+    }
+
     public void tearDown(Surface surface) throws CameraAccessException {
         if (surface == null) throw new IllegalArgumentException("Surface is null");
 
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index 27087a2..0978ff8 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -215,6 +215,16 @@
         }
     }
 
+    public void updateOutputConfiguration(int streamId, OutputConfiguration config)
+            throws CameraAccessException {
+        try {
+            mRemoteDevice.updateOutputConfiguration(streamId, config);
+        } catch (Throwable t) {
+            CameraManager.throwAsPublicException(t);
+            throw new UnsupportedOperationException("Unexpected exception", t);
+        }
+    }
+
     public void finalizeOutputConfigurations(int streamId, OutputConfiguration deferredConfig)
             throws CameraAccessException {
         try {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 49d4096..119cca8 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -646,6 +646,11 @@
     }
 
     @Override
+    public void updateOutputConfiguration(int streamId, OutputConfiguration config) {
+        // TODO: b/63912484 implement updateOutputConfiguration.
+    }
+
+    @Override
     public void waitUntilIdle() throws RemoteException {
         if (DEBUG) {
             Log.d(TAG, "waitUntilIdle called.");
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 2b317d6..7409671 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -42,6 +42,53 @@
  * A class for describing camera output, which contains a {@link Surface} and its specific
  * configuration for creating capture session.
  *
+ * <p>There are several ways to instantiate, modify and use OutputConfigurations. The most common
+ * and recommended usage patterns are summarized in the following list:</p>
+ *<ul>
+ * <li>Passing a {@link Surface} to the constructor and using the OutputConfiguration instance as
+ * argument to {@link CameraDevice#createCaptureSessionByOutputConfigurations}. This is the most
+ * frequent usage and clients should consider it first before other more complicated alternatives.
+ * </li>
+ *
+ * <li>Passing only a surface source class as an argument to the constructor. This is usually
+ * followed by a call to create a capture session
+ * (see {@link CameraDevice#createCaptureSessionByOutputConfigurations} and a {@link Surface} add
+ * call {@link #addSurface} with a valid {@link Surface}. The sequence completes with
+ * {@link CameraCaptureSession#finalizeOutputConfigurations}. This is the deferred usage case which
+ * aims to enhance performance by allowing the resource-intensive capture session create call to
+ * execute in parallel with any {@link Surface} initialization, such as waiting for a
+ * {@link android.view.SurfaceView} to be ready as part of the UI initialization.</li>
+ *
+ * <li>The third and most complex usage pattern inlvolves surface sharing. Once instantiated an
+ * OutputConfiguration can be enabled for surface sharing via {@link #enableSurfaceSharing}. This
+ * must be done before creating a new capture session and enables calls to
+ * {@link CameraCaptureSession#updateOutputConfiguration}. An OutputConfiguration with enabled
+ * surface sharing can be modified via {@link #addSurface} or {@link #removeSurface}. The updates
+ * to this OutputConfiguration will only come into effect after
+ * {@link CameraCaptureSession#updateOutputConfiguration} returns without throwing exceptions.
+ * Such updates can be done as long as the session is active. Clients should always consider the
+ * additional requirements and limitations placed on the output surfaces (for more details see
+ * {@link #enableSurfaceSharing}, {@link #addSurface}, {@link #removeSurface},
+ * {@link CameraCaptureSession#updateOutputConfiguration}). A trade-off exists between additional
+ * complexity and flexibility. If exercised correctly surface sharing can switch between different
+ * output surfaces without interrupting any ongoing repeating capture requests. This saves time and
+ * can significantly improve the user experience.</li>
+ *
+ * <li>Surface sharing can be used in combination with deferred surfaces. The rules from both cases
+ * are combined and clients must call {@link #enableSurfaceSharing} before creating a capture
+ * session. Attach and/or remove output surfaces via  {@link #addSurface}/{@link #removeSurface} and
+ * finalize the configuration using {@link CameraCaptureSession#finalizeOutputConfigurations}.
+ * {@link CameraCaptureSession#updateOutputConfiguration} can be called after the configuration
+ * finalize method returns without exceptions.</li>
+ *
+ * </ul>
+ *
+ * <p>Please note that surface sharing is currently only enabled for outputs that use the
+ * {@link ImageFormat#PRIVATE} format. This includes surface sources like
+ * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder},
+ * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using
+ * the aforementioned format.</p>
+ *
  * @see CameraDevice#createCaptureSessionByOutputConfigurations
  *
  */
@@ -123,7 +170,7 @@
      * {@link OutputConfiguration#addSurface} should not exceed this value.</p>
      *
      */
-    private static final int MAX_SURFACES_COUNT = 2;
+    private static final int MAX_SURFACES_COUNT = 4;
 
     /**
      * Create a new {@link OutputConfiguration} instance with a {@link Surface},
@@ -280,7 +327,10 @@
      * <p>For advanced use cases, a camera application may require more streams than the combination
      * guaranteed by {@link CameraDevice#createCaptureSession}. In this case, more than one
      * compatible surface can be attached to an OutputConfiguration so that they map to one
-     * camera stream, and the outputs share memory buffers when possible. </p>
+     * camera stream, and the outputs share memory buffers when possible. Due to buffer sharing
+     * clients should be careful when adding surface outputs that modify their input data. If such
+     * case exists, camera clients should have an additional mechanism to synchronize read and write
+     * access between individual consumers.</p>
      *
      * <p>Two surfaces are compatible in the below cases:</p>
      *
@@ -301,9 +351,9 @@
      * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link
      * CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
      *
-     * <p>Up to 2 surfaces can be shared for an OutputConfiguration. The supported surfaces for
-     * sharing must be of type SurfaceTexture, SurfaceView, MediaRecorder, MediaCodec, or
-     * implementation defined ImageReader.</p>
+     * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
+     * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
+     * MediaRecorder, MediaCodec, or implementation defined ImageReader.</p>
      */
     public void enableSurfaceSharing() {
         mIsShared = true;
@@ -329,8 +379,10 @@
      * <p> This function can be called before or after {@link
      * CameraDevice#createCaptureSessionByOutputConfigurations}. If it's called after,
      * the application must finalize the capture session with
-     * {@link CameraCaptureSession#finalizeOutputConfigurations}.
-     * </p>
+     * {@link CameraCaptureSession#finalizeOutputConfigurations}. It is possible to call this method
+     * after the output configurations have been finalized only in cases of enabled surface sharing
+     * see {@link #enableSurfaceSharing}. The modified output configuration must be updated with
+     * {@link CameraCaptureSession#updateOutputConfiguration}.</p>
      *
      * <p> If the OutputConfiguration was constructed with a deferred surface by {@link
      * OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained
@@ -388,6 +440,31 @@
     }
 
     /**
+     * Remove a surface from this OutputConfiguration.
+     *
+     * <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
+     *  OutputConfiguration. The only notable exception is the surface associated with
+     *  the OutputConfigration see {@link #getSurface} which was passed as part of the constructor
+     *  or was added first in the deferred case
+     *  {@link OutputConfiguration#OutputConfiguration(Size, Class)}.</p>
+     *
+     * @param surface The surface to be removed.
+     *
+     * @throws IllegalArgumentException If the surface is associated with this OutputConfiguration
+     *                                  (see {@link #getSurface}) or the surface didn't get added
+     *                                  with {@link #addSurface}.
+     */
+    public void removeSurface(@NonNull Surface surface) {
+        if (getSurface() == surface) {
+            throw new IllegalArgumentException(
+                    "Cannot remove surface associated with this output configuration");
+        }
+        if (!mSurfaces.remove(surface)) {
+            throw new IllegalArgumentException("Surface is not part of this output configuration");
+        }
+    }
+
+    /**
      * Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
      * instance.
      *
@@ -447,6 +524,17 @@
     }
 
     /**
+     * Get the maximum supported shared {@link Surface} count.
+     *
+     * @return the maximum number of surfaces that can be added per each OutputConfiguration.
+     *
+     * @see #enableSurfaceSharing
+     */
+    public static int getMaxSharedSurfaceCount() {
+        return MAX_SURFACES_COUNT;
+    }
+
+    /**
      * Get the {@link Surface} associated with this {@link OutputConfiguration}.
      *
      * If more than one surface is associated with this {@link OutputConfiguration}, return the
diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java
new file mode 100644
index 0000000..d9b7c5b
--- /dev/null
+++ b/core/java/android/hardware/usb/AccessoryFilter.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * This class is used to describe a USB accessory.
+ * When used in HashMaps all values must be specified,
+ * but wildcards can be used for any of the fields in
+ * the package meta-data.
+ *
+ * @hide
+ */
+public class AccessoryFilter {
+    // USB accessory manufacturer (or null for unspecified)
+    public final String mManufacturer;
+    // USB accessory model (or null for unspecified)
+    public final String mModel;
+    // USB accessory version (or null for unspecified)
+    public final String mVersion;
+
+    public AccessoryFilter(String manufacturer, String model, String version) {
+        mManufacturer = manufacturer;
+        mModel = model;
+        mVersion = version;
+    }
+
+    public AccessoryFilter(UsbAccessory accessory) {
+        mManufacturer = accessory.getManufacturer();
+        mModel = accessory.getModel();
+        mVersion = accessory.getVersion();
+    }
+
+    public static AccessoryFilter read(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        String manufacturer = null;
+        String model = null;
+        String version = null;
+
+        int count = parser.getAttributeCount();
+        for (int i = 0; i < count; i++) {
+            String name = parser.getAttributeName(i);
+            String value = parser.getAttributeValue(i);
+
+            if ("manufacturer".equals(name)) {
+                manufacturer = value;
+            } else if ("model".equals(name)) {
+                model = value;
+            } else if ("version".equals(name)) {
+                version = value;
+            }
+        }
+        return new AccessoryFilter(manufacturer, model, version);
+    }
+
+    public void write(XmlSerializer serializer)throws IOException {
+        serializer.startTag(null, "usb-accessory");
+        if (mManufacturer != null) {
+            serializer.attribute(null, "manufacturer", mManufacturer);
+        }
+        if (mModel != null) {
+            serializer.attribute(null, "model", mModel);
+        }
+        if (mVersion != null) {
+            serializer.attribute(null, "version", mVersion);
+        }
+        serializer.endTag(null, "usb-accessory");
+    }
+
+    public boolean matches(UsbAccessory acc) {
+        if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
+        if (mModel != null && !acc.getModel().equals(mModel)) return false;
+        return !(mVersion != null && !acc.getVersion().equals(mVersion));
+    }
+
+    /**
+     * Is the accessories described {@code accessory} covered by this filter?
+     *
+     * @param accessory A filter describing the accessory
+     *
+     * @return {@code true} iff this the filter covers the accessory
+     */
+    public boolean contains(AccessoryFilter accessory) {
+        if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
+            return false;
+        }
+        if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
+        return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion));
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        // can't compare if we have wildcard strings
+        if (mManufacturer == null || mModel == null || mVersion == null) {
+            return false;
+        }
+        if (obj instanceof AccessoryFilter) {
+            AccessoryFilter filter = (AccessoryFilter)obj;
+            return (mManufacturer.equals(filter.mManufacturer) &&
+                    mModel.equals(filter.mModel) &&
+                    mVersion.equals(filter.mVersion));
+        }
+        if (obj instanceof UsbAccessory) {
+            UsbAccessory accessory = (UsbAccessory)obj;
+            return (mManufacturer.equals(accessory.getManufacturer()) &&
+                    mModel.equals(accessory.getModel()) &&
+                    mVersion.equals(accessory.getVersion()));
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
+                (mModel == null ? 0 : mModel.hashCode()) ^
+                (mVersion == null ? 0 : mVersion.hashCode()));
+    }
+
+    @Override
+    public String toString() {
+        return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
+                "\", mModel=\"" + mModel +
+                "\", mVersion=\"" + mVersion + "\"]";
+    }
+}
diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java
new file mode 100644
index 0000000..439c629
--- /dev/null
+++ b/core/java/android/hardware/usb/DeviceFilter.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import android.util.Slog;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * This class is used to describe a USB device.
+ * When used in HashMaps all values must be specified,
+ * but wildcards can be used for any of the fields in
+ * the package meta-data.
+ *
+ * @hide
+ */
+public class DeviceFilter {
+    private static final String TAG = DeviceFilter.class.getSimpleName();
+
+    // USB Vendor ID (or -1 for unspecified)
+    public final int mVendorId;
+    // USB Product ID (or -1 for unspecified)
+    public final int mProductId;
+    // USB device or interface class (or -1 for unspecified)
+    public final int mClass;
+    // USB device subclass (or -1 for unspecified)
+    public final int mSubclass;
+    // USB device protocol (or -1 for unspecified)
+    public final int mProtocol;
+    // USB device manufacturer name string (or null for unspecified)
+    public final String mManufacturerName;
+    // USB device product name string (or null for unspecified)
+    public final String mProductName;
+    // USB device serial number string (or null for unspecified)
+    public final String mSerialNumber;
+
+    public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
+            String manufacturer, String product, String serialnum) {
+        mVendorId = vid;
+        mProductId = pid;
+        mClass = clasz;
+        mSubclass = subclass;
+        mProtocol = protocol;
+        mManufacturerName = manufacturer;
+        mProductName = product;
+        mSerialNumber = serialnum;
+    }
+
+    public DeviceFilter(UsbDevice device) {
+        mVendorId = device.getVendorId();
+        mProductId = device.getProductId();
+        mClass = device.getDeviceClass();
+        mSubclass = device.getDeviceSubclass();
+        mProtocol = device.getDeviceProtocol();
+        mManufacturerName = device.getManufacturerName();
+        mProductName = device.getProductName();
+        mSerialNumber = device.getSerialNumber();
+    }
+
+    public static DeviceFilter read(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int vendorId = -1;
+        int productId = -1;
+        int deviceClass = -1;
+        int deviceSubclass = -1;
+        int deviceProtocol = -1;
+        String manufacturerName = null;
+        String productName = null;
+        String serialNumber = null;
+
+        int count = parser.getAttributeCount();
+        for (int i = 0; i < count; i++) {
+            String name = parser.getAttributeName(i);
+            String value = parser.getAttributeValue(i);
+            // Attribute values are ints or strings
+            if ("manufacturer-name".equals(name)) {
+                manufacturerName = value;
+            } else if ("product-name".equals(name)) {
+                productName = value;
+            } else if ("serial-number".equals(name)) {
+                serialNumber = value;
+            } else {
+                int intValue;
+                int radix = 10;
+                if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
+                        (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
+                    // allow hex values starting with 0x or 0X
+                    radix = 16;
+                    value = value.substring(2);
+                }
+                try {
+                    intValue = Integer.parseInt(value, radix);
+                } catch (NumberFormatException e) {
+                    Slog.e(TAG, "invalid number for field " + name, e);
+                    continue;
+                }
+                if ("vendor-id".equals(name)) {
+                    vendorId = intValue;
+                } else if ("product-id".equals(name)) {
+                    productId = intValue;
+                } else if ("class".equals(name)) {
+                    deviceClass = intValue;
+                } else if ("subclass".equals(name)) {
+                    deviceSubclass = intValue;
+                } else if ("protocol".equals(name)) {
+                    deviceProtocol = intValue;
+                }
+            }
+        }
+        return new DeviceFilter(vendorId, productId,
+                deviceClass, deviceSubclass, deviceProtocol,
+                manufacturerName, productName, serialNumber);
+    }
+
+    public void write(XmlSerializer serializer) throws IOException {
+        serializer.startTag(null, "usb-device");
+        if (mVendorId != -1) {
+            serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
+        }
+        if (mProductId != -1) {
+            serializer.attribute(null, "product-id", Integer.toString(mProductId));
+        }
+        if (mClass != -1) {
+            serializer.attribute(null, "class", Integer.toString(mClass));
+        }
+        if (mSubclass != -1) {
+            serializer.attribute(null, "subclass", Integer.toString(mSubclass));
+        }
+        if (mProtocol != -1) {
+            serializer.attribute(null, "protocol", Integer.toString(mProtocol));
+        }
+        if (mManufacturerName != null) {
+            serializer.attribute(null, "manufacturer-name", mManufacturerName);
+        }
+        if (mProductName != null) {
+            serializer.attribute(null, "product-name", mProductName);
+        }
+        if (mSerialNumber != null) {
+            serializer.attribute(null, "serial-number", mSerialNumber);
+        }
+        serializer.endTag(null, "usb-device");
+    }
+
+    private boolean matches(int clasz, int subclass, int protocol) {
+        return ((mClass == -1 || clasz == mClass) &&
+                (mSubclass == -1 || subclass == mSubclass) &&
+                (mProtocol == -1 || protocol == mProtocol));
+    }
+
+    public boolean matches(UsbDevice device) {
+        if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
+        if (mProductId != -1 && device.getProductId() != mProductId) return false;
+        if (mManufacturerName != null && device.getManufacturerName() == null) return false;
+        if (mProductName != null && device.getProductName() == null) return false;
+        if (mSerialNumber != null && device.getSerialNumber() == null) return false;
+        if (mManufacturerName != null && device.getManufacturerName() != null &&
+                !mManufacturerName.equals(device.getManufacturerName())) return false;
+        if (mProductName != null && device.getProductName() != null &&
+                !mProductName.equals(device.getProductName())) return false;
+        if (mSerialNumber != null && device.getSerialNumber() != null &&
+                !mSerialNumber.equals(device.getSerialNumber())) return false;
+
+        // check device class/subclass/protocol
+        if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
+                device.getDeviceProtocol())) return true;
+
+        // if device doesn't match, check the interfaces
+        int count = device.getInterfaceCount();
+        for (int i = 0; i < count; i++) {
+            UsbInterface intf = device.getInterface(i);
+            if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
+                    intf.getInterfaceProtocol())) return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * If the device described by {@code device} covered by this filter?
+     *
+     * @param device The device
+     *
+     * @return {@code true} iff this filter covers the {@code device}
+     */
+    public boolean contains(DeviceFilter device) {
+        // -1 and null means "match anything"
+
+        if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
+        if (mProductId != -1 && device.mProductId != mProductId) return false;
+        if (mManufacturerName != null && !Objects.equals(mManufacturerName,
+                device.mManufacturerName)) {
+            return false;
+        }
+        if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
+            return false;
+        }
+        if (mSerialNumber != null
+                && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
+            return false;
+        }
+
+        // check device class/subclass/protocol
+        return matches(device.mClass, device.mSubclass, device.mProtocol);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        // can't compare if we have wildcard strings
+        if (mVendorId == -1 || mProductId == -1 ||
+                mClass == -1 || mSubclass == -1 || mProtocol == -1) {
+            return false;
+        }
+        if (obj instanceof DeviceFilter) {
+            DeviceFilter filter = (DeviceFilter)obj;
+
+            if (filter.mVendorId != mVendorId ||
+                    filter.mProductId != mProductId ||
+                    filter.mClass != mClass ||
+                    filter.mSubclass != mSubclass ||
+                    filter.mProtocol != mProtocol) {
+                return(false);
+            }
+            if ((filter.mManufacturerName != null &&
+                    mManufacturerName == null) ||
+                    (filter.mManufacturerName == null &&
+                            mManufacturerName != null) ||
+                    (filter.mProductName != null &&
+                            mProductName == null)  ||
+                    (filter.mProductName == null &&
+                            mProductName != null) ||
+                    (filter.mSerialNumber != null &&
+                            mSerialNumber == null)  ||
+                    (filter.mSerialNumber == null &&
+                            mSerialNumber != null)) {
+                return(false);
+            }
+            if  ((filter.mManufacturerName != null &&
+                    mManufacturerName != null &&
+                    !mManufacturerName.equals(filter.mManufacturerName)) ||
+                    (filter.mProductName != null &&
+                            mProductName != null &&
+                            !mProductName.equals(filter.mProductName)) ||
+                    (filter.mSerialNumber != null &&
+                            mSerialNumber != null &&
+                            !mSerialNumber.equals(filter.mSerialNumber))) {
+                return false;
+            }
+            return true;
+        }
+        if (obj instanceof UsbDevice) {
+            UsbDevice device = (UsbDevice)obj;
+            if (device.getVendorId() != mVendorId ||
+                    device.getProductId() != mProductId ||
+                    device.getDeviceClass() != mClass ||
+                    device.getDeviceSubclass() != mSubclass ||
+                    device.getDeviceProtocol() != mProtocol) {
+                return(false);
+            }
+            if ((mManufacturerName != null && device.getManufacturerName() == null) ||
+                    (mManufacturerName == null && device.getManufacturerName() != null) ||
+                    (mProductName != null && device.getProductName() == null) ||
+                    (mProductName == null && device.getProductName() != null) ||
+                    (mSerialNumber != null && device.getSerialNumber() == null) ||
+                    (mSerialNumber == null && device.getSerialNumber() != null)) {
+                return(false);
+            }
+            if ((device.getManufacturerName() != null &&
+                    !mManufacturerName.equals(device.getManufacturerName())) ||
+                    (device.getProductName() != null &&
+                            !mProductName.equals(device.getProductName())) ||
+                    (device.getSerialNumber() != null &&
+                            !mSerialNumber.equals(device.getSerialNumber()))) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return (((mVendorId << 16) | mProductId) ^
+                ((mClass << 16) | (mSubclass << 8) | mProtocol));
+    }
+
+    @Override
+    public String toString() {
+        return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
+                ",mClass=" + mClass + ",mSubclass=" + mSubclass +
+                ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
+                ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
+                "]";
+    }
+}
diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl
index 6f07b31..aeaf09d 100644
--- a/core/java/android/net/IIpConnectivityMetrics.aidl
+++ b/core/java/android/net/IIpConnectivityMetrics.aidl
@@ -30,11 +30,11 @@
     int logEvent(in ConnectivityMetricsEvent event);
 
     /**
-     * At most one callback can be registered (by DevicePolicyManager).
+     * Callback can be registered by DevicePolicyManager or NetworkWatchlistService only.
      * @return status {@code true} if registering/unregistering of the callback was successful,
      *         {@code false} otherwise (might happen if IIpConnectivityMetrics is not available,
      *         if it happens make sure you call it when the service is up in the caller)
      */
-    boolean registerNetdEventCallback(in INetdEventCallback callback);
-    boolean unregisterNetdEventCallback();
+    boolean addNetdEventCallback(in int callerType, in INetdEventCallback callback);
+    boolean removeNetdEventCallback(in int callerType);
 }
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 49436be..1fd9423 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -19,6 +19,10 @@
 /** {@hide} */
 oneway interface INetdEventCallback {
 
+    // Possible addNetdEventCallback callers.
+    const int CALLBACK_CALLER_DEVICE_POLICY = 0;
+    const int CALLBACK_CALLER_NETWORK_WATCHLIST = 1;
+
     /**
      * Reports a single DNS lookup function call.
      * This method must not block or perform long-running operations.
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 16b1452..64f8f39 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -78,7 +78,11 @@
     /**
      * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
      *
-     * <p>Valid lengths for this key are {128, 192, 256}.
+     * <p>Valid lengths for keying material are {160, 224, 288}.
+     *
+     * <p>As per RFC4106 (Section 8.1), keying material consists of a 128, 192, or 256 bit AES key
+     * followed by a 32-bit salt. RFC compliance requires that the salt must be unique per
+     * invocation with the same key.
      *
      * <p>Valid ICV (truncation) lengths are {64, 96, 128}.
      */
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index d7b3256..eccd5f4 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -136,7 +136,7 @@
         }
 
         @Override
-        protected void finalize() {
+        protected void finalize() throws Throwable {
             if (mCloseGuard != null) {
                 mCloseGuard.warnIfOpen();
             }
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 05c8afb..6e4a231 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -16,18 +16,18 @@
 
 package android.net;
 
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.InputStream;
-import java.io.FileDescriptor;
-import java.net.SocketOptions;
-
 import android.system.ErrnoException;
+import android.system.Int32Ref;
 import android.system.Os;
 import android.system.OsConstants;
 import android.system.StructLinger;
 import android.system.StructTimeval;
-import android.util.MutableInt;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketOptions;
 
 /**
  * Socket implementation used for android.net.LocalSocket and
@@ -62,7 +62,7 @@
             FileDescriptor myFd = fd;
             if (myFd == null) throw new IOException("socket closed");
 
-            MutableInt avail = new MutableInt(0);
+            Int32Ref avail = new Int32Ref(0);
             try {
                 Os.ioctlInt(myFd, OsConstants.FIONREAD, avail);
             } catch (ErrnoException e) {
@@ -167,7 +167,7 @@
             if (myFd == null) throw new IOException("socket closed");
 
             // Loop until the output buffer is empty.
-            MutableInt pending = new MutableInt(0);
+            Int32Ref pending = new Int32Ref(0);
             while (true) {
                 try {
                     // See linux/net/unix/af_unix.c
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index e76d17d..f6a69ba 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -16,29 +16,128 @@
 
 package android.net;
 
-import com.android.internal.annotations.VisibleForTesting;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.BitUtils;
 
 import java.util.Arrays;
+import java.util.Random;
+import java.util.StringJoiner;
 
 /**
+ * Represents a mac address.
+ *
  * @hide
  */
-public final class MacAddress {
-
-    // TODO: add isLocallyAssigned().
-    // TODO: add getRandomAddress() factory method.
+public final class MacAddress implements Parcelable {
 
     private static final int ETHER_ADDR_LEN = 6;
-    private static final byte FF = (byte) 0xff;
-    @VisibleForTesting
-    static final byte[] ETHER_ADDR_BROADCAST = { FF, FF, FF, FF, FF, FF };
+    private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
 
+    /** The broadcast mac address.  */
+    public static final MacAddress BROADCAST_ADDRESS = new MacAddress(ETHER_ADDR_BROADCAST);
+
+    /** The zero mac address. */
+    public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
+
+    /** Represents categories of mac addresses. */
     public enum MacAddressType {
         UNICAST,
         MULTICAST,
         BROADCAST;
     }
 
+    private static final long VALID_LONG_MASK = BROADCAST_ADDRESS.mAddr;
+    private static final long LOCALLY_ASSIGNED_MASK = new MacAddress("2:0:0:0:0:0").mAddr;
+    private static final long MULTICAST_MASK = new MacAddress("1:0:0:0:0:0").mAddr;
+    private static final long OUI_MASK = new MacAddress("ff:ff:ff:0:0:0").mAddr;
+    private static final long NIC_MASK = new MacAddress("0:0:0:ff:ff:ff").mAddr;
+    private static final MacAddress BASE_ANDROID_MAC = new MacAddress("da:a1:19:0:0:0");
+
+    // Internal representation of the mac address as a single 8 byte long.
+    // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
+    // mac address are encoded in the 6 least significant bytes of the long, where the first
+    // byte of the array is mapped to the 3rd highest logical byte of the long, the second
+    // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
+    private final long mAddr;
+
+    private MacAddress(long addr) {
+        mAddr = addr;
+    }
+
+    /** Creates a MacAddress for the given byte representation. */
+    public MacAddress(byte[] addr) {
+        this(longAddrFromByteAddr(addr));
+    }
+
+    /** Creates a MacAddress for the given string representation. */
+    public MacAddress(String addr) {
+        this(longAddrFromByteAddr(byteAddrFromStringAddr(addr)));
+    }
+
+    /** Returns the MacAddressType of this MacAddress. */
+    public MacAddressType addressType() {
+        if (equals(BROADCAST_ADDRESS)) {
+            return MacAddressType.BROADCAST;
+        }
+        if (isMulticastAddress()) {
+            return MacAddressType.MULTICAST;
+        }
+        return MacAddressType.UNICAST;
+    }
+
+    /** Returns true if this MacAddress corresponds to a multicast address. */
+    public boolean isMulticastAddress() {
+        return (mAddr & MULTICAST_MASK) != 0;
+    }
+
+    /** Returns true if this MacAddress corresponds to a locally assigned address. */
+    public boolean isLocallyAssigned() {
+        return (mAddr & LOCALLY_ASSIGNED_MASK) != 0;
+    }
+
+    /** Returns a byte array representation of this MacAddress. */
+    public byte[] toByteArray() {
+        return byteAddrFromLongAddr(mAddr);
+    }
+
+    @Override
+    public String toString() {
+        return stringAddrFromByteAddr(byteAddrFromLongAddr(mAddr));
+    }
+
+    @Override
+    public int hashCode() {
+        return (int) ((mAddr >> 32) ^ mAddr);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(mAddr);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<MacAddress> CREATOR =
+            new Parcelable.Creator<MacAddress>() {
+                public MacAddress createFromParcel(Parcel in) {
+                    return new MacAddress(in.readLong());
+                }
+
+                public MacAddress[] newArray(int size) {
+                    return new MacAddress[size];
+                }
+            };
+
     /** Return true if the given byte array is not null and has the length of a mac address. */
     public static boolean isMacAddress(byte[] addr) {
         return addr != null && addr.length == ETHER_ADDR_LEN;
@@ -46,17 +145,130 @@
 
     /**
      * Return the MacAddressType of the mac address represented by the given byte array,
-     * or null if the given byte array does not represent an mac address. */
+     * or null if the given byte array does not represent an mac address.
+     */
     public static MacAddressType macAddressType(byte[] addr) {
         if (!isMacAddress(addr)) {
             return null;
         }
-        if (Arrays.equals(addr, ETHER_ADDR_BROADCAST)) {
-            return MacAddressType.BROADCAST;
+        return new MacAddress(addr).addressType();
+    }
+
+    /** DOCME */
+    public static byte[] byteAddrFromStringAddr(String addr) {
+        if (addr == null) {
+            throw new IllegalArgumentException("cannot convert the null String");
         }
-        if ((addr[0] & 0x01) == 1) {
-            return MacAddressType.MULTICAST;
+        String[] parts = addr.split(":");
+        if (parts.length != ETHER_ADDR_LEN) {
+            throw new IllegalArgumentException(addr + " was not a valid MAC address");
         }
-        return MacAddressType.UNICAST;
+        byte[] bytes = new byte[ETHER_ADDR_LEN];
+        for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+            int x = Integer.valueOf(parts[i], 16);
+            if (x < 0 || 0xff < x) {
+                throw new IllegalArgumentException(addr + "was not a valid MAC address");
+            }
+            bytes[i] = (byte) x;
+        }
+        return bytes;
+    }
+
+    /** DOCME */
+    public static String stringAddrFromByteAddr(byte[] addr) {
+        if (!isMacAddress(addr)) {
+            return null;
+        }
+        StringJoiner j = new StringJoiner(":");
+        for (byte b : addr) {
+            j.add(Integer.toHexString(BitUtils.uint8(b)));
+        }
+        return j.toString();
+    }
+
+    /** @hide */
+    public static byte[] byteAddrFromLongAddr(long addr) {
+        byte[] bytes = new byte[ETHER_ADDR_LEN];
+        int index = ETHER_ADDR_LEN;
+        while (index-- > 0) {
+            bytes[index] = (byte) addr;
+            addr = addr >> 8;
+        }
+        return bytes;
+    }
+
+    /** @hide */
+    public static long longAddrFromByteAddr(byte[] addr) {
+        if (!isMacAddress(addr)) {
+            throw new IllegalArgumentException(
+                    Arrays.toString(addr) + " was not a valid MAC address");
+        }
+        long longAddr = 0;
+        for (byte b : addr) {
+            longAddr = (longAddr << 8) + BitUtils.uint8(b);
+        }
+        return longAddr;
+    }
+
+    /** @hide */
+    public static long longAddrFromStringAddr(String addr) {
+        if (addr == null) {
+            throw new IllegalArgumentException("cannot convert the null String");
+        }
+        String[] parts = addr.split(":");
+        if (parts.length != ETHER_ADDR_LEN) {
+            throw new IllegalArgumentException(addr + " was not a valid MAC address");
+        }
+        long longAddr = 0;
+        int index = ETHER_ADDR_LEN;
+        while (index-- > 0) {
+            int x = Integer.valueOf(parts[index], 16);
+            if (x < 0 || 0xff < x) {
+                throw new IllegalArgumentException(addr + "was not a valid MAC address");
+            }
+            longAddr = x + (longAddr << 8);
+        }
+        return longAddr;
+    }
+
+    /** @hide */
+    public static String stringAddrFromLongAddr(long addr) {
+        addr = Long.reverseBytes(addr) >> 16;
+        StringJoiner j = new StringJoiner(":");
+        for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+            j.add(Integer.toHexString((byte) addr));
+            addr = addr >> 8;
+        }
+        return j.toString();
+    }
+
+    /**
+     * Returns a randomely generated mac address with the Android OUI value "DA-A1-19".
+     * The locally assigned bit is always set to 1.
+     */
+    public static MacAddress getRandomAddress() {
+        return getRandomAddress(BASE_ANDROID_MAC, new Random());
+    }
+
+    /**
+     * Returns a randomely generated mac address using the given Random object and the same
+     * OUI values as the given MacAddress. The locally assigned bit is always set to 1.
+     */
+    public static MacAddress getRandomAddress(MacAddress base, Random r) {
+        long longAddr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong()) | LOCALLY_ASSIGNED_MASK;
+        return new MacAddress(longAddr);
+    }
+
+    // Convenience function for working around the lack of byte literals.
+    private static byte[] addr(int... in) {
+        if (in.length != ETHER_ADDR_LEN) {
+            throw new IllegalArgumentException(Arrays.toString(in)
+                    + " was not an array with length equal to " + ETHER_ADDR_LEN);
+        }
+        byte[] out = new byte[ETHER_ADDR_LEN];
+        for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+            out[i] = (byte) in[i];
+        }
+        return out;
     }
 }
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
new file mode 100644
index 0000000..42e43c8
--- /dev/null
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -0,0 +1,75 @@
+/*
+ * 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.net;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.net.INetworkWatchlistManager;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Class that manage network watchlist in system.
+ * @hide
+ */
+@SystemService(Context.NETWORK_WATCHLIST_SERVICE)
+public class NetworkWatchlistManager {
+
+    private static final String TAG = "NetworkWatchlistManager";
+    private static final String SHARED_MEMORY_TAG = "NETWORK_WATCHLIST_SHARED_MEMORY";
+
+    private final Context mContext;
+    private final INetworkWatchlistManager mNetworkWatchlistManager;
+
+    /**
+     * @hide
+     */
+    public NetworkWatchlistManager(Context context, INetworkWatchlistManager manager) {
+        mContext = context;
+        mNetworkWatchlistManager = manager;
+    }
+
+    /**
+     * @hide
+     */
+    public NetworkWatchlistManager(Context context) {
+        mContext = Preconditions.checkNotNull(context, "missing context");
+        mNetworkWatchlistManager = (INetworkWatchlistManager)
+                INetworkWatchlistManager.Stub.asInterface(
+                        ServiceManager.getService(Context.NETWORK_WATCHLIST_SERVICE));
+    }
+
+    /**
+     * Report network watchlist records if necessary.
+     *
+     * Watchlist report process will run summarize records into a single report, then the
+     * report will be processed by differential privacy framework and store it on disk.
+     *
+     * @hide
+     */
+    public void reportWatchlistIfNecessary() {
+        try {
+            mNetworkWatchlistManager.reportWatchlistIfNecessary();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Cannot report records", e);
+            e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 0b1569c..4817813 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -63,7 +63,12 @@
  * This implementation does check the server's certificate hostname, but only
  * for createSocket variants that specify a hostname.  When using methods that
  * use {@link InetAddress} or which return an unconnected socket, you MUST
- * verify the server's identity yourself to ensure a secure connection.</p>
+ * verify the server's identity yourself to ensure a secure connection.
+ *
+ * Refer to
+ * <a href="https://developer.android.com/training/articles/security-gms-provider.html">
+ * Updating Your Security Provider to Protect Against SSL Exploits</a>
+ * for further information.</p>
  *
  * <p>One way to verify the server's identity is to use
  * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 4df3bf0..1999e78 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -60,29 +60,25 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
 
-    public final int netId;
     public final @EventType int eventType;
     public final long durationMs;
 
-    public NetworkEvent(int netId, @EventType int eventType, long durationMs) {
-        this.netId = netId;
+    public NetworkEvent(@EventType int eventType, long durationMs) {
         this.eventType = eventType;
         this.durationMs = durationMs;
     }
 
-    public NetworkEvent(int netId, @EventType int eventType) {
-        this(netId, eventType, 0);
+    public NetworkEvent(@EventType int eventType) {
+        this(eventType, 0);
     }
 
     private NetworkEvent(Parcel in) {
-        netId = in.readInt();
         eventType = in.readInt();
         durationMs = in.readLong();
     }
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(netId);
         out.writeInt(eventType);
         out.writeLong(durationMs);
     }
@@ -105,8 +101,8 @@
 
     @Override
     public String toString() {
-        return String.format("NetworkEvent(%d, %s, %dms)",
-                netId, Decoder.constants.get(eventType), durationMs);
+        return String.format("NetworkEvent(%s, %dms)",
+                Decoder.constants.get(eventType), durationMs);
     }
 
     final static class Decoder {
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
index cbf3fc8..8f1a5c4 100644
--- a/core/java/android/net/metrics/WakeupEvent.java
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -16,6 +16,10 @@
 
 package android.net.metrics;
 
+import android.net.MacAddress;
+
+import java.util.StringJoiner;
+
 /**
  * An event logged when NFLOG notifies userspace of a wakeup packet for
  * watched interfaces.
@@ -23,12 +27,35 @@
  */
 public class WakeupEvent {
     public String iface;
-    public long timestampMs;
     public int uid;
+    public int ethertype;
+    public byte[] dstHwAddr;
+    public String srcIp;
+    public String dstIp;
+    public int ipNextHeader;
+    public int srcPort;
+    public int dstPort;
+    public long timestampMs;
 
     @Override
     public String toString() {
-        return String.format("WakeupEvent(%tT.%tL, %s, uid: %d)",
-                timestampMs, timestampMs, iface, uid);
+        StringJoiner j = new StringJoiner(", ", "WakeupEvent(", ")");
+        j.add(String.format("%tT.%tL", timestampMs, timestampMs));
+        j.add(iface);
+        j.add("uid: " + Integer.toString(uid));
+        j.add("eth=0x" + Integer.toHexString(ethertype));
+        j.add("dstHw=" + MacAddress.stringAddrFromByteAddr(dstHwAddr));
+        if (ipNextHeader > 0) {
+            j.add("ipNxtHdr=" + ipNextHeader);
+            j.add("srcIp=" + srcIp);
+            j.add("dstIp=" + dstIp);
+            if (srcPort > -1) {
+                j.add("srcPort=" + srcPort);
+            }
+            if (dstPort > -1) {
+                j.add("dstPort=" + dstPort);
+            }
+        }
+        return j.toString();
     }
 }
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 97e83f9..1ba9777 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -16,8 +16,12 @@
 
 package android.net.metrics;
 
+import android.net.MacAddress;
 import android.os.Process;
 import android.os.SystemClock;
+import android.util.SparseIntArray;
+
+import java.util.StringJoiner;
 
 /**
  * An event logged per interface and that aggregates WakeupEvents for that interface.
@@ -38,6 +42,13 @@
     public long noUidWakeups = 0;
     public long durationSec = 0;
 
+    public long l2UnicastCount = 0;
+    public long l2MulticastCount = 0;
+    public long l2BroadcastCount = 0;
+
+    public final SparseIntArray ethertypes = new SparseIntArray();
+    public final SparseIntArray ipNextHeaders = new SparseIntArray();
+
     public WakeupStats(String iface) {
         this.iface = iface;
     }
@@ -68,20 +79,56 @@
                 }
                 break;
         }
+
+        switch (MacAddress.macAddressType(ev.dstHwAddr)) {
+            case UNICAST:
+                l2UnicastCount++;
+                break;
+            case MULTICAST:
+                l2MulticastCount++;
+                break;
+            case BROADCAST:
+                l2BroadcastCount++;
+                break;
+            default:
+                break;
+        }
+
+        increment(ethertypes, ev.ethertype);
+        if (ev.ipNextHeader >= 0) {
+            increment(ipNextHeaders, ev.ipNextHeader);
+        }
     }
 
     @Override
     public String toString() {
         updateDuration();
-        return new StringBuilder()
-                .append("WakeupStats(").append(iface)
-                .append(", total: ").append(totalWakeups)
-                .append(", root: ").append(rootWakeups)
-                .append(", system: ").append(systemWakeups)
-                .append(", apps: ").append(applicationWakeups)
-                .append(", non-apps: ").append(nonApplicationWakeups)
-                .append(", no uid: ").append(noUidWakeups)
-                .append(", ").append(durationSec).append("s)")
-                .toString();
+        StringJoiner j = new StringJoiner(", ", "WakeupStats(", ")");
+        j.add(iface);
+        j.add("" + durationSec + "s");
+        j.add("total: " + totalWakeups);
+        j.add("root: " + rootWakeups);
+        j.add("system: " + systemWakeups);
+        j.add("apps: " + applicationWakeups);
+        j.add("non-apps: " + nonApplicationWakeups);
+        j.add("no uid: " + noUidWakeups);
+        j.add(String.format("l2 unicast/multicast/broadcast: %d/%d/%d",
+                l2UnicastCount, l2MulticastCount, l2BroadcastCount));
+        for (int i = 0; i < ethertypes.size(); i++) {
+            int eth = ethertypes.keyAt(i);
+            int count = ethertypes.valueAt(i);
+            j.add(String.format("ethertype 0x%x: %d", eth, count));
+        }
+        for (int i = 0; i < ipNextHeaders.size(); i++) {
+            int proto = ipNextHeaders.keyAt(i);
+            int count = ipNextHeaders.valueAt(i);
+            j.add(String.format("ipNxtHdr %d: %d", proto, count));
+        }
+        return j.toString();
+    }
+
+    private static void increment(SparseIntArray counters, int key) {
+        int newcount = counters.get(key, 0) + 1;
+        counters.put(key, newcount);
     }
 }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 2bfb013..b5bcd02 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -193,6 +193,19 @@
     }
 
     /**
+     * Reset the given interface back to the default blocking behavior,
+     * reverting any changes made by {@link #allowBlocking(IBinder)}.
+     *
+     * @hide
+     */
+    public static IBinder defaultBlocking(IBinder binder) {
+        if (binder instanceof BinderProxy) {
+            ((BinderProxy) binder).mWarnOnBlocking = sWarnOnBlocking;
+        }
+        return binder;
+    }
+
+    /**
      * Inherit the current {@link #allowBlocking(IBinder)} value from one given
      * interface to another.
      *
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e426356..5d96fd3 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -94,4 +94,5 @@
     boolean isUserUnlocked(int userId);
     boolean isUserRunning(int userId);
     boolean isUserNameSet(int userHandle);
+    boolean hasRestrictedProfiles();
 }
diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl
index 5857aae..6bbd99e 100644
--- a/core/java/android/os/ParcelFileDescriptor.aidl
+++ b/core/java/android/os/ParcelFileDescriptor.aidl
@@ -17,4 +17,4 @@
 
 package android.os;
 
-parcelable ParcelFileDescriptor;
+parcelable ParcelFileDescriptor cpp_header "android/os/parcel_file_descriptor.h";
diff --git a/core/java/android/os/ParcelUuid.aidl b/core/java/android/os/ParcelUuid.aidl
index f7e080a..6f36297 100644
--- a/core/java/android/os/ParcelUuid.aidl
+++ b/core/java/android/os/ParcelUuid.aidl
@@ -16,4 +16,4 @@
 
 package android.os;
 
-parcelable ParcelUuid;
+parcelable ParcelUuid cpp_header "android/os/parcel_uuid.h";
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 01b6535..dd4825e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -526,7 +526,11 @@
             ServiceType.SCREEN_BRIGHTNESS,
             ServiceType.SOUND,
             ServiceType.BATTERY_STATS,
-            ServiceType.DATA_SAVER})
+            ServiceType.DATA_SAVER,
+            ServiceType.FORCE_ALL_APPS_STANDBY_JOBS,
+            ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS,
+            ServiceType.OPTIONAL_SENSORS,
+    })
     public @interface ServiceType {
         int NULL = 0;
         int GPS = 1;
@@ -539,6 +543,21 @@
         int SOUND = 8;
         int BATTERY_STATS = 9;
         int DATA_SAVER = 10;
+
+        /**
+         * Whether the job scheduler should force app standby on all apps on battery saver or not.
+         */
+        int FORCE_ALL_APPS_STANDBY_JOBS = 11;
+
+        /**
+         * Whether the alarm manager should force app standby on all apps on battery saver or not.
+         */
+        int FORCE_ALL_APPS_STANDBY_ALARMS = 12;
+
+        /**
+         * Whether to disable non-essential sensors. (e.g. edge sensors.)
+         */
+        int OPTIONAL_SENSORS = 13;
     }
 
     final Context mContext;
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index a01b8ed..77ac2651 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -18,6 +18,8 @@
 
 import android.view.Display;
 
+import java.util.function.Consumer;
+
 /**
  * Power manager local system service interface.
  *
@@ -125,6 +127,23 @@
 
     public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
 
+    /**
+     * Same as {@link #registerLowPowerModeObserver} but can take a lambda.
+     */
+    public void registerLowPowerModeObserver(int serviceType, Consumer<PowerSaveState> listener) {
+        registerLowPowerModeObserver(new LowPowerModeListener() {
+            @Override
+            public int getServiceType() {
+                return serviceType;
+            }
+
+            @Override
+            public void onLowPowerModeChanged(PowerSaveState state) {
+                listener.accept(state);
+            }
+        });
+    }
+
     public interface LowPowerModeListener {
         int getServiceType();
         void onLowPowerModeChanged(PowerSaveState state);
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index d5820b6..f90604a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -16,11 +16,11 @@
 package android.os;
 
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
-import android.app.ApplicationErrorReport;
 import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -29,8 +29,6 @@
 import android.content.pm.ApplicationInfo;
 import android.net.TrafficStats;
 import android.net.Uri;
-import android.os.StrictMode.ThreadPolicy;
-import android.os.StrictMode.VmPolicy;
 import android.os.strictmode.CleartextNetworkViolation;
 import android.os.strictmode.ContentUriWithoutPermissionViolation;
 import android.os.strictmode.CustomViolation;
@@ -44,7 +42,7 @@
 import android.os.strictmode.ResourceMismatchViolation;
 import android.os.strictmode.ServiceConnectionLeakedViolation;
 import android.os.strictmode.SqliteObjectLeakedViolation;
-import android.os.strictmode.UnbufferedIOViolation;
+import android.os.strictmode.UnbufferedIoViolation;
 import android.os.strictmode.UntaggedSocketViolation;
 import android.os.strictmode.Violation;
 import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation;
@@ -75,6 +73,8 @@
 import java.util.Arrays;
 import java.util.Deque;
 import java.util.HashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -163,12 +163,14 @@
     private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear";
 
     /**
-     * Quick feature-flag that can be used to disable the defaults provided by
-     * {@link #initThreadDefaults(ApplicationInfo)} and
-     * {@link #initVmDefaults(ApplicationInfo)}.
+     * Quick feature-flag that can be used to disable the defaults provided by {@link
+     * #initThreadDefaults(ApplicationInfo)} and {@link #initVmDefaults(ApplicationInfo)}.
      */
     private static final boolean DISABLE = false;
 
+    // Only apply VM penalties for the same violation at this interval.
+    private static final long MIN_VM_INTERVAL_MS = 1000;
+
     // Only log a duplicate stack trace to the logs every second.
     private static final long MIN_LOG_INTERVAL_MS = 1000;
 
@@ -374,6 +376,28 @@
 
     private static volatile ViolationLogger sLogger = LOGCAT_LOGGER;
 
+    private static final ThreadLocal<OnThreadViolationListener> sThreadViolationListener =
+            new ThreadLocal<>();
+    private static final ThreadLocal<Executor> sThreadViolationExecutor = new ThreadLocal<>();
+
+    /**
+     * When #{@link ThreadPolicy.Builder#penaltyListener} is enabled, the listener is called on the
+     * provided executor when a Thread violation occurs.
+     */
+    public interface OnThreadViolationListener {
+        /** Called on a thread policy violation. */
+        void onThreadViolation(Violation v);
+    }
+
+    /**
+     * When #{@link VmPolicy.Builder#penaltyListener} is enabled, the listener is called on the
+     * provided executor when a VM violation occurs.
+     */
+    public interface OnVmViolationListener {
+        /** Called on a VM policy violation. */
+        void onVmViolation(Violation v);
+    }
+
     /** {@hide} */
     @TestApi
     public static void setViolationLogger(ViolationLogger listener) {
@@ -403,12 +427,16 @@
      */
     public static final class ThreadPolicy {
         /** The default, lax policy which doesn't catch anything. */
-        public static final ThreadPolicy LAX = new ThreadPolicy(0);
+        public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null);
 
         final int mask;
+        final OnThreadViolationListener mListener;
+        final Executor mCallbackExecutor;
 
-        private ThreadPolicy(int mask) {
+        private ThreadPolicy(int mask, OnThreadViolationListener listener, Executor executor) {
             this.mask = mask;
+            mListener = listener;
+            mCallbackExecutor = executor;
         }
 
         @Override
@@ -436,6 +464,8 @@
          */
         public static final class Builder {
             private int mMask = 0;
+            private OnThreadViolationListener mListener;
+            private Executor mExecutor;
 
             /**
              * Create a Builder that detects nothing and has no violations. (but note that {@link
@@ -601,6 +631,20 @@
                 return enable(PENALTY_DROPBOX);
             }
 
+            /**
+             * Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified
+             * executor every violation.
+             */
+            public Builder penaltyListener(
+                    @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+                if (executor == null) {
+                    throw new NullPointerException("executor must not be null");
+                }
+                mListener = listener;
+                mExecutor = executor;
+                return this;
+            }
+
             private Builder enable(int bit) {
                 mMask |= bit;
                 return this;
@@ -620,7 +664,8 @@
             public ThreadPolicy build() {
                 // If there are detection bits set but no violation bits
                 // set, enable simple logging.
-                if (mMask != 0
+                if (mListener == null
+                        && mMask != 0
                         && (mMask
                                         & (PENALTY_DEATH
                                                 | PENALTY_LOG
@@ -629,7 +674,7 @@
                                 == 0) {
                     penaltyLog();
                 }
-                return new ThreadPolicy(mMask);
+                return new ThreadPolicy(mMask, mListener, mExecutor);
             }
         }
     }
@@ -641,19 +686,27 @@
      */
     public static final class VmPolicy {
         /** The default, lax policy which doesn't catch anything. */
-        public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP);
+        public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null);
 
         final int mask;
+        final OnVmViolationListener mListener;
+        final Executor mCallbackExecutor;
 
         // Map from class to max number of allowed instances in memory.
         final HashMap<Class, Integer> classInstanceLimit;
 
-        private VmPolicy(int mask, HashMap<Class, Integer> classInstanceLimit) {
+        private VmPolicy(
+                int mask,
+                HashMap<Class, Integer> classInstanceLimit,
+                OnVmViolationListener listener,
+                Executor executor) {
             if (classInstanceLimit == null) {
                 throw new NullPointerException("classInstanceLimit == null");
             }
             this.mask = mask;
             this.classInstanceLimit = classInstanceLimit;
+            mListener = listener;
+            mCallbackExecutor = executor;
         }
 
         @Override
@@ -681,6 +734,8 @@
          */
         public static final class Builder {
             private int mMask;
+            private OnVmViolationListener mListener;
+            private Executor mExecutor;
 
             private HashMap<Class, Integer> mClassInstanceLimit; // null until needed
             private boolean mClassInstanceLimitNeedCow = false; // need copy-on-write
@@ -694,6 +749,8 @@
                 mMask = base.mask;
                 mClassInstanceLimitNeedCow = true;
                 mClassInstanceLimit = base.classInstanceLimit;
+                mListener = base.mListener;
+                mExecutor = base.mCallbackExecutor;
             }
 
             /**
@@ -910,6 +967,19 @@
                 return enable(PENALTY_DROPBOX);
             }
 
+            /**
+             * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
+             */
+            public Builder penaltyListener(
+                    @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+                if (executor == null) {
+                    throw new NullPointerException("executor must not be null");
+                }
+                mListener = listener;
+                mExecutor = executor;
+                return this;
+            }
+
             private Builder enable(int bit) {
                 mMask |= bit;
                 return this;
@@ -929,7 +999,8 @@
             public VmPolicy build() {
                 // If there are detection bits set but no violation bits
                 // set, enable simple logging.
-                if (mMask != 0
+                if (mListener == null
+                        && mMask != 0
                         && (mMask
                                         & (PENALTY_DEATH
                                                 | PENALTY_LOG
@@ -940,7 +1011,9 @@
                 }
                 return new VmPolicy(
                         mMask,
-                        mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP);
+                        mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP,
+                        mListener,
+                        mExecutor);
             }
         }
     }
@@ -973,6 +1046,8 @@
      */
     public static void setThreadPolicy(final ThreadPolicy policy) {
         setThreadPolicyMask(policy.mask);
+        sThreadViolationListener.set(policy.mListener);
+        sThreadViolationExecutor.set(policy.mCallbackExecutor);
     }
 
     /** @hide */
@@ -1029,7 +1104,10 @@
         // introduce VmPolicy cleanly) but this isn't particularly
         // optimal for users who might call this method often.  This
         // should be in a thread-local and not allocate on each call.
-        return new ThreadPolicy(getThreadPolicyMask());
+        return new ThreadPolicy(
+                getThreadPolicyMask(),
+                sThreadViolationListener.get(),
+                sThreadViolationExecutor.get());
     }
 
     /**
@@ -1042,7 +1120,10 @@
      *     end of a block
      */
     public static ThreadPolicy allowThreadDiskWrites() {
-        return new ThreadPolicy(allowThreadDiskWritesMask());
+        return new ThreadPolicy(
+                allowThreadDiskWritesMask(),
+                sThreadViolationListener.get(),
+                sThreadViolationExecutor.get());
     }
 
     /** @hide */
@@ -1063,7 +1144,10 @@
      * @return the old policy, to be passed to setThreadPolicy to restore the policy.
      */
     public static ThreadPolicy allowThreadDiskReads() {
-        return new ThreadPolicy(allowThreadDiskReadsMask());
+        return new ThreadPolicy(
+                allowThreadDiskReadsMask(),
+                sThreadViolationListener.get(),
+                sThreadViolationExecutor.get());
     }
 
     /** @hide */
@@ -1076,16 +1160,27 @@
         return oldPolicyMask;
     }
 
+    private static ThreadPolicy allowThreadViolations() {
+        ThreadPolicy oldPolicy = getThreadPolicy();
+        setThreadPolicyMask(0);
+        return oldPolicy;
+    }
+
+    private static VmPolicy allowVmViolations() {
+        VmPolicy oldPolicy = getVmPolicy();
+        sVmPolicy = VmPolicy.LAX;
+        return oldPolicy;
+    }
+
     /**
-     * Determine if the given app is "bundled" as part of the system image.
-     * These bundled apps are developed in lock-step with the OS, and they
-     * aren't updated outside of an OTA, so we want to chase any
-     * {@link StrictMode} regressions by enabling detection when running on
-     * {@link Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
-     * <p>
-     * Unbundled apps included in the system image are expected to detect and
-     * triage their own {@link StrictMode} issues separate from the OS release
-     * process, which is why we don't enable them here.
+     * Determine if the given app is "bundled" as part of the system image. These bundled apps are
+     * developed in lock-step with the OS, and they aren't updated outside of an OTA, so we want to
+     * chase any {@link StrictMode} regressions by enabling detection when running on {@link
+     * Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
+     *
+     * <p>Unbundled apps included in the system image are expected to detect and triage their own
+     * {@link StrictMode} issues separate from the OS release process, which is why we don't enable
+     * them here.
      *
      * @hide
      */
@@ -1122,8 +1217,8 @@
      */
     public static void initThreadDefaults(ApplicationInfo ai) {
         final ThreadPolicy.Builder builder = new ThreadPolicy.Builder();
-        final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion
-                : Build.VERSION_CODES.CUR_DEVELOPMENT;
+        final int targetSdkVersion =
+                (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
 
         // Starting in HC, we don't allow network usage on the main thread
         if (targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
@@ -1162,8 +1257,8 @@
      */
     public static void initVmDefaults(ApplicationInfo ai) {
         final VmPolicy.Builder builder = new VmPolicy.Builder();
-        final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion
-                : Build.VERSION_CODES.CUR_DEVELOPMENT;
+        final int targetSdkVersion =
+                (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
 
         // Starting in N, we don't allow file:// Uri exposure
         if (targetSdkVersion >= Build.VERSION_CODES.N) {
@@ -1204,7 +1299,9 @@
                         sVmPolicy.mask
                                 | DETECT_VM_FILE_URI_EXPOSURE
                                 | PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
-                        sVmPolicy.classInstanceLimit);
+                        sVmPolicy.classInstanceLimit,
+                        sVmPolicy.mListener,
+                        sVmPolicy.mCallbackExecutor);
     }
 
     /**
@@ -1219,7 +1316,9 @@
                         sVmPolicy.mask
                                 & ~(DETECT_VM_FILE_URI_EXPOSURE
                                         | PENALTY_DEATH_ON_FILE_URI_EXPOSURE),
-                        sVmPolicy.classInstanceLimit);
+                        sVmPolicy.classInstanceLimit,
+                        sVmPolicy.mListener,
+                        sVmPolicy.mCallbackExecutor);
     }
 
     /**
@@ -1340,7 +1439,7 @@
             if (tooManyViolationsThisLoop()) {
                 return;
             }
-            startHandlingViolationException(new UnbufferedIOViolation());
+            startHandlingViolationException(new UnbufferedIoViolation());
         }
 
         // Part of BlockGuard.Policy interface:
@@ -1409,7 +1508,7 @@
             //       go into this immediate mode?
             if (looper == null || (info.mPolicy & THREAD_PENALTY_MASK) == PENALTY_DEATH) {
                 info.durationMillis = -1; // unknown (redundant, already set)
-                handleViolation(info);
+                onThreadPolicyViolation(info);
                 return;
             }
 
@@ -1447,30 +1546,28 @@
             THREAD_HANDLER
                     .get()
                     .postAtFrontOfQueue(
-                            new Runnable() {
-                                public void run() {
-                                    long loopFinishTime = SystemClock.uptimeMillis();
+                            () -> {
+                                long loopFinishTime = SystemClock.uptimeMillis();
 
-                                    // Note: we do this early, before handling the
-                                    // violation below, as handling the violation
-                                    // may include PENALTY_DEATH and we don't want
-                                    // to keep the red border on.
-                                    if (windowManager != null) {
-                                        try {
-                                            windowManager.showStrictModeViolation(false);
-                                        } catch (RemoteException unused) {
-                                        }
+                                // Note: we do this early, before handling the
+                                // violation below, as handling the violation
+                                // may include PENALTY_DEATH and we don't want
+                                // to keep the red border on.
+                                if (windowManager != null) {
+                                    try {
+                                        windowManager.showStrictModeViolation(false);
+                                    } catch (RemoteException unused) {
                                     }
-
-                                    for (int n = 0; n < records.size(); ++n) {
-                                        ViolationInfo v = records.get(n);
-                                        v.violationNumThisLoop = n + 1;
-                                        v.durationMillis =
-                                                (int) (loopFinishTime - v.violationUptimeMillis);
-                                        handleViolation(v);
-                                    }
-                                    records.clear();
                                 }
+
+                                for (int n = 0; n < records.size(); ++n) {
+                                    ViolationInfo v = records.get(n);
+                                    v.violationNumThisLoop = n + 1;
+                                    v.durationMillis =
+                                            (int) (loopFinishTime - v.violationUptimeMillis);
+                                    onThreadPolicyViolation(v);
+                                }
+                                records.clear();
                             });
         }
 
@@ -1479,13 +1576,13 @@
         // violation fired and now (after the violating code ran) due
         // to people who push/pop temporary policy in regions of code,
         // hence the policy being passed around.
-        void handleViolation(final ViolationInfo info) {
-            if (LOG_V) Log.d(TAG, "handleViolation; policy=" + info.mPolicy);
+        void onThreadPolicyViolation(final ViolationInfo info) {
+            if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; policy=" + info.mPolicy);
 
             if (info.penaltyEnabled(PENALTY_GATHER)) {
                 ArrayList<ViolationInfo> violations = gatheredViolations.get();
                 if (violations == null) {
-                    violations = new ArrayList<ViolationInfo>(1);
+                    violations = new ArrayList<>(1);
                     gatheredViolations.set(violations);
                 }
                 for (ViolationInfo previous : violations) {
@@ -1519,6 +1616,8 @@
                 sLogger.log(info);
             }
 
+            final Violation violation = info.mViolation;
+
             // The violationMaskSubset, passed to ActivityManager, is a
             // subset of the original StrictMode policy bitmask, with
             // only the bit violated and penalty bits to be executed
@@ -1552,15 +1651,32 @@
             }
 
             if ((info.getPolicyMask() & PENALTY_DEATH) != 0) {
-                executeDeathPenalty(info);
+                throw new RuntimeException("StrictMode ThreadPolicy violation", violation);
+            }
+
+            // penaltyDeath will cause penaltyCallback to no-op since we cannot guarantee the
+            // executor finishes before crashing.
+            final OnThreadViolationListener listener = sThreadViolationListener.get();
+            final Executor executor = sThreadViolationExecutor.get();
+            if (listener != null && executor != null) {
+                try {
+                    executor.execute(
+                            () -> {
+                                // Lift violated policy to prevent infinite recursion.
+                                ThreadPolicy oldPolicy = allowThreadViolations();
+                                try {
+                                    listener.onThreadViolation(violation);
+                                } finally {
+                                    setThreadPolicy(oldPolicy);
+                                }
+                            });
+                } catch (RejectedExecutionException e) {
+                    Log.e(TAG, "ThreadPolicy penaltyCallback failed", e);
+                }
             }
         }
     }
 
-    private static void executeDeathPenalty(ViolationInfo info) {
-        throw new RuntimeException("StrictMode death penalty", info.mViolation);
-    }
-
     /**
      * In the common case, as set by conditionallyEnableDebugLogging, we're just dropboxing any
      * violations but not showing a dialog, not loggging, and not killing the process. In these
@@ -1736,9 +1852,8 @@
      * #setThreadPolicy}.
      */
     public static void enableDefaults() {
-        StrictMode.setThreadPolicy(
-                new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
-        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
+        setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+        setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
     }
 
     /** @hide */
@@ -1857,11 +1972,11 @@
     }
 
     /** @hide */
-    public static void onVmPolicyViolation(Violation originStack, boolean forceDeath) {
+    public static void onVmPolicyViolation(Violation violation, boolean forceDeath) {
         final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0;
         final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath;
         final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0;
-        final ViolationInfo info = new ViolationInfo(originStack, sVmPolicy.mask);
+        final ViolationInfo info = new ViolationInfo(violation, sVmPolicy.mask);
 
         // Erase stuff not relevant for process-wide violations
         info.numAnimationsRunning = 0;
@@ -1870,37 +1985,37 @@
 
         final Integer fingerprint = info.hashCode();
         final long now = SystemClock.uptimeMillis();
-        long lastViolationTime = 0;
+        long lastViolationTime;
         long timeSinceLastViolationMillis = Long.MAX_VALUE;
         synchronized (sLastVmViolationTime) {
             if (sLastVmViolationTime.containsKey(fingerprint)) {
                 lastViolationTime = sLastVmViolationTime.get(fingerprint);
                 timeSinceLastViolationMillis = now - lastViolationTime;
             }
-            if (timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+            if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
                 sLastVmViolationTime.put(fingerprint, now);
             }
         }
-
-        if (penaltyLog && sLogger != null) {
-            sLogger.log(info);
+        if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) {
+            // Rate limit all penalties.
+            return;
         }
-        if (penaltyLog && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+
+        if (penaltyLog && sLogger != null && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
             sLogger.log(info);
         }
 
         int violationMaskSubset = PENALTY_DROPBOX | (ALL_VM_DETECT_BITS & sVmPolicy.mask);
 
-        if (penaltyDropbox && !penaltyDeath) {
-            // Common case for userdebug/eng builds.  If no death and
-            // just dropboxing, we can do the ActivityManager call
-            // asynchronously.
-            dropboxViolationAsync(violationMaskSubset, info);
-            return;
-        }
-
-        if (penaltyDropbox && lastViolationTime == 0) {
-            handleApplicationStrictModeViolation(violationMaskSubset, info);
+        if (penaltyDropbox) {
+            if (penaltyDeath) {
+                handleApplicationStrictModeViolation(violationMaskSubset, info);
+            } else {
+                // Common case for userdebug/eng builds.  If no death and
+                // just dropboxing, we can do the ActivityManager call
+                // asynchronously.
+                dropboxViolationAsync(violationMaskSubset, info);
+            }
         }
 
         if (penaltyDeath) {
@@ -1908,6 +2023,26 @@
             Process.killProcess(Process.myPid());
             System.exit(10);
         }
+
+        // If penaltyDeath, we can't guarantee this callback finishes before the process dies for
+        // all executors. penaltyDeath supersedes penaltyCallback.
+        if (sVmPolicy.mListener != null && sVmPolicy.mCallbackExecutor != null) {
+            final OnVmViolationListener listener = sVmPolicy.mListener;
+            try {
+                sVmPolicy.mCallbackExecutor.execute(
+                        () -> {
+                            // Lift violated policy to prevent infinite recursion.
+                            VmPolicy oldPolicy = allowVmViolations();
+                            try {
+                                listener.onVmViolation(violation);
+                            } finally {
+                                setVmPolicy(oldPolicy);
+                            }
+                        });
+            } catch (RejectedExecutionException e) {
+                Log.e(TAG, "VmPolicy penaltyCallback failed", e);
+            }
+        }
     }
 
     /** Called from Parcel.writeNoException() */
@@ -2407,7 +2542,7 @@
                 return DETECT_CUSTOM;
             } else if (mViolation instanceof ResourceMismatchViolation) {
                 return DETECT_RESOURCE_MISMATCH;
-            } else if (mViolation instanceof UnbufferedIOViolation) {
+            } else if (mViolation instanceof UnbufferedIoViolation) {
                 return DETECT_UNBUFFERED_IO;
             } else if (mViolation instanceof SqliteObjectLeakedViolation) {
                 return DETECT_VM_CURSOR_LEAKS;
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index b3d76d7..c52c22d 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -16,12 +16,18 @@
 
 package android.os;
 
+import android.annotation.NonNull;
 import android.app.IAlarmManager;
 import android.content.Context;
 import android.util.Slog;
 
 import dalvik.annotation.optimization.CriticalNative;
 
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+
 /**
  * Core timekeeping facilities.
  *
@@ -168,6 +174,31 @@
     native public static long uptimeMillis();
 
     /**
+     * Return {@link Clock} that starts at system boot, not counting time spent
+     * in deep sleep.
+     */
+    public static @NonNull Clock uptimeMillisClock() {
+        return new Clock() {
+            @Override
+            public ZoneId getZone() {
+                return ZoneOffset.UTC;
+            }
+            @Override
+            public Clock withZone(ZoneId zone) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            public long millis() {
+                return SystemClock.uptimeMillis();
+            }
+            @Override
+            public Instant instant() {
+                return Instant.ofEpochMilli(millis());
+            }
+        };
+    }
+
+    /**
      * Returns milliseconds since boot, including time spent in sleep.
      *
      * @return elapsed milliseconds since boot.
@@ -176,6 +207,31 @@
     native public static long elapsedRealtime();
 
     /**
+     * Return {@link Clock} that starts at system boot, including time spent in
+     * sleep.
+     */
+    public static @NonNull Clock elapsedRealtimeClock() {
+        return new Clock() {
+            @Override
+            public ZoneId getZone() {
+                return ZoneOffset.UTC;
+            }
+            @Override
+            public Clock withZone(ZoneId zone) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            public long millis() {
+                return SystemClock.elapsedRealtime();
+            }
+            @Override
+            public Instant instant() {
+                return Instant.ofEpochMilli(millis());
+            }
+        };
+    }
+
+    /**
      * Returns nanoseconds since boot, including time spent in sleep.
      *
      * @return elapsed nanoseconds since boot.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c54b72d..22967af 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -140,6 +140,18 @@
     public static final String DISALLOW_CONFIG_WIFI = "no_config_wifi";
 
     /**
+     * Specifies if a user is disallowed from changing the device
+     * language. The default value is <code>false</code>.
+     *
+     * <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_CONFIG_LOCALE = "no_config_locale";
+
+    /**
      * Specifies if a user is disallowed from installing applications.
      * The default value is <code>false</code>.
      *
@@ -792,6 +804,19 @@
     public static final String DISALLOW_AUTOFILL = "no_autofill";
 
     /**
+     * Specifies if user switching is blocked on the current user.
+     *
+     * <p> This restriction can only be set by the device owner, it will be applied to all users.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_USER_SWITCH = "no_user_switch";
+
+    /**
      * Application restriction key that is used to indicate the pending arrival
      * of real restrictions for the app.
      *
@@ -917,7 +942,7 @@
     /**
      * Returns whether switching users is currently allowed.
      * <p>For instance switching users is not allowed if the current user is in a phone call,
-     * or system user hasn't been unlocked yet
+     * system user hasn't been unlocked yet, or {@link #DISALLOW_USER_SWITCH} is set.
      * @hide
      */
     public boolean canSwitchUsers() {
@@ -927,7 +952,9 @@
         boolean isSystemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM);
         boolean inCall = TelephonyManager.getDefault().getCallState()
                 != TelephonyManager.CALL_STATE_IDLE;
-        return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall;
+        boolean isUserSwitchDisallowed = hasUserRestriction(DISALLOW_USER_SWITCH);
+        return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall
+                && !isUserSwitchDisallowed;
     }
 
     /**
@@ -1022,12 +1049,22 @@
     }
 
     /**
-     * Used to check if the user making this call is linked to another user. Linked users may have
+     * @hide
+     * @deprecated Use {@link #isRestrictedProfile()}
+     */
+    @Deprecated
+    public boolean isLinkedUser() {
+        return isRestrictedProfile();
+    }
+
+    /**
+     * Returns whether the caller is running as restricted profile. Restricted profile may have
      * a reduced number of available apps, app restrictions and account restrictions.
      * @return whether the user making this call is a linked user
      * @hide
      */
-    public boolean isLinkedUser() {
+    @SystemApi
+    public boolean isRestrictedProfile() {
         try {
             return mService.isRestricted();
         } catch (RemoteException re) {
@@ -1048,6 +1085,20 @@
     }
 
     /**
+     * Returns whether the calling user has at least one restricted profile associated with it.
+     * @return
+     * @hide
+     */
+    @SystemApi
+    public boolean hasRestrictedProfiles() {
+        try {
+            return mService.hasRestrictedProfiles();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Checks if a user is a guest user.
      * @return whether user is a guest user.
      * @hide
@@ -1067,6 +1118,7 @@
         return user != null && user.isGuest();
     }
 
+
     /**
      * Checks if the calling app is running in a demo user. When running in a demo user,
      * apps can be more helpful to the user, or explain their features in more detail.
@@ -2298,6 +2350,9 @@
         if (!supportsMultipleUsers()) {
             return false;
         }
+        if (hasUserRestriction(DISALLOW_USER_SWITCH)) {
+            return false;
+        }
         // If Demo Mode is on, don't show user switcher
         if (isDeviceInDemoMode(mContext)) {
             return false;
diff --git a/core/java/android/os/strictmode/CleartextNetworkViolation.java b/core/java/android/os/strictmode/CleartextNetworkViolation.java
index 893780d..6a0d381 100644
--- a/core/java/android/os/strictmode/CleartextNetworkViolation.java
+++ b/core/java/android/os/strictmode/CleartextNetworkViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class CleartextNetworkViolation extends Violation {
+    /** @hide */
     public CleartextNetworkViolation(String msg) {
         super(msg);
     }
diff --git a/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java b/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
index 017c4f9..e78dc79 100644
--- a/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
+++ b/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
@@ -17,8 +17,8 @@
 
 import android.net.Uri;
 
-/** @hide */
 public final class ContentUriWithoutPermissionViolation extends Violation {
+    /** @hide */
     public ContentUriWithoutPermissionViolation(Uri uri, String location) {
         super(
                 uri
diff --git a/core/java/android/os/strictmode/CustomViolation.java b/core/java/android/os/strictmode/CustomViolation.java
index bc1656d..d4ad067 100644
--- a/core/java/android/os/strictmode/CustomViolation.java
+++ b/core/java/android/os/strictmode/CustomViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class CustomViolation extends Violation {
+    /** @hide */
     public CustomViolation(String name) {
         super(name);
     }
diff --git a/core/java/android/os/strictmode/DiskReadViolation.java b/core/java/android/os/strictmode/DiskReadViolation.java
index 2edd33e..fad32db 100644
--- a/core/java/android/os/strictmode/DiskReadViolation.java
+++ b/core/java/android/os/strictmode/DiskReadViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class DiskReadViolation extends Violation {
+    /** @hide */
     public DiskReadViolation() {
         super(null);
     }
diff --git a/core/java/android/os/strictmode/DiskWriteViolation.java b/core/java/android/os/strictmode/DiskWriteViolation.java
index 6465718..cb9ca38 100644
--- a/core/java/android/os/strictmode/DiskWriteViolation.java
+++ b/core/java/android/os/strictmode/DiskWriteViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class DiskWriteViolation extends Violation {
+    /** @hide */
     public DiskWriteViolation() {
         super(null);
     }
diff --git a/core/java/android/os/strictmode/FileUriExposedViolation.java b/core/java/android/os/strictmode/FileUriExposedViolation.java
index 5f71ee5..e3e6f83 100644
--- a/core/java/android/os/strictmode/FileUriExposedViolation.java
+++ b/core/java/android/os/strictmode/FileUriExposedViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class FileUriExposedViolation extends Violation {
+    /** @hide */
     public FileUriExposedViolation(String msg) {
         super(msg);
     }
diff --git a/core/java/android/os/strictmode/InstanceCountViolation.java b/core/java/android/os/strictmode/InstanceCountViolation.java
index d3da800..9ee2c8e 100644
--- a/core/java/android/os/strictmode/InstanceCountViolation.java
+++ b/core/java/android/os/strictmode/InstanceCountViolation.java
@@ -15,7 +15,6 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public class InstanceCountViolation extends Violation {
     private final long mInstances;
 
@@ -24,6 +23,7 @@
                 "android.os.StrictMode", "setClassInstanceLimit", "StrictMode.java", 1)
     };
 
+    /** @hide */
     public InstanceCountViolation(Class klass, long instances, int limit) {
         super(klass.toString() + "; instances=" + instances + "; limit=" + limit);
         setStackTrace(FAKE_STACK);
diff --git a/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java b/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
index 1d1dfc0..f416c94 100644
--- a/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
+++ b/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class IntentReceiverLeakedViolation extends Violation {
+    /** @hide */
     public IntentReceiverLeakedViolation(Throwable originStack) {
         super(null);
         setStackTrace(originStack.getStackTrace());
diff --git a/core/java/android/os/strictmode/LeakedClosableViolation.java b/core/java/android/os/strictmode/LeakedClosableViolation.java
index de12533..c795a6b 100644
--- a/core/java/android/os/strictmode/LeakedClosableViolation.java
+++ b/core/java/android/os/strictmode/LeakedClosableViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class LeakedClosableViolation extends Violation {
+    /** @hide */
     public LeakedClosableViolation(String message, Throwable allocationSite) {
         super(message);
         initCause(allocationSite);
diff --git a/core/java/android/os/strictmode/NetworkViolation.java b/core/java/android/os/strictmode/NetworkViolation.java
index 637d0b6..abcf009 100644
--- a/core/java/android/os/strictmode/NetworkViolation.java
+++ b/core/java/android/os/strictmode/NetworkViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class NetworkViolation extends Violation {
+    /** @hide */
     public NetworkViolation() {
         super(null);
     }
diff --git a/core/java/android/os/strictmode/ResourceMismatchViolation.java b/core/java/android/os/strictmode/ResourceMismatchViolation.java
index 01ae7f8..97c4499 100644
--- a/core/java/android/os/strictmode/ResourceMismatchViolation.java
+++ b/core/java/android/os/strictmode/ResourceMismatchViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class ResourceMismatchViolation extends Violation {
+    /** @hide */
     public ResourceMismatchViolation(Object tag) {
         super(tag.toString());
     }
diff --git a/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java b/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
index 5bab3bd..2d6b58f 100644
--- a/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
+++ b/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class ServiceConnectionLeakedViolation extends Violation {
+    /** @hide */
     public ServiceConnectionLeakedViolation(Throwable originStack) {
         super(null);
         setStackTrace(originStack.getStackTrace());
diff --git a/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java b/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
index 8360088..0200220 100644
--- a/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
+++ b/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
@@ -15,9 +15,9 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class SqliteObjectLeakedViolation extends Violation {
 
+    /** @hide */
     public SqliteObjectLeakedViolation(String message, Throwable originStack) {
         super(message);
         initCause(originStack);
diff --git a/core/java/android/os/strictmode/UnbufferedIOViolation.java b/core/java/android/os/strictmode/UnbufferedIoViolation.java
similarity index 69%
rename from core/java/android/os/strictmode/UnbufferedIOViolation.java
rename to core/java/android/os/strictmode/UnbufferedIoViolation.java
index 571ba50..a5c326d 100644
--- a/core/java/android/os/strictmode/UnbufferedIOViolation.java
+++ b/core/java/android/os/strictmode/UnbufferedIoViolation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,9 +15,14 @@
  */
 package android.os.strictmode;
 
-/** @hide */
-public final class UnbufferedIOViolation extends Violation {
-    public UnbufferedIOViolation() {
+import android.os.StrictMode.ThreadPolicy.Builder;
+
+/**
+ * See #{@link Builder#detectUnbufferedIo()}
+ */
+public final class UnbufferedIoViolation extends Violation {
+    /** @hide */
+    public UnbufferedIoViolation() {
         super(null);
     }
 }
diff --git a/core/java/android/os/strictmode/UntaggedSocketViolation.java b/core/java/android/os/strictmode/UntaggedSocketViolation.java
index 9752753..836a8b9 100644
--- a/core/java/android/os/strictmode/UntaggedSocketViolation.java
+++ b/core/java/android/os/strictmode/UntaggedSocketViolation.java
@@ -15,13 +15,13 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class UntaggedSocketViolation extends Violation {
     /** @hide */
     public static final String MESSAGE =
             "Untagged socket detected; use"
                     + " TrafficStats.setThreadSocketTag() to track all network usage";
 
+    /** @hide */
     public UntaggedSocketViolation() {
         super(MESSAGE);
     }
diff --git a/core/java/android/os/strictmode/Violation.java b/core/java/android/os/strictmode/Violation.java
index ebae7fc..31c7d58 100644
--- a/core/java/android/os/strictmode/Violation.java
+++ b/core/java/android/os/strictmode/Violation.java
@@ -16,13 +16,9 @@
 
 package android.os.strictmode;
 
-/**
- * Root class for all StrictMode violations.
- *
- * @hide
- */
+/** Root class for all StrictMode violations. */
 public abstract class Violation extends Throwable {
-    protected Violation(String message) {
+    Violation(String message) {
         super(message);
     }
 }
diff --git a/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java b/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
index d4c557a..c328d14 100644
--- a/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
+++ b/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
@@ -15,8 +15,8 @@
  */
 package android.os.strictmode;
 
-/** @hide */
 public final class WebViewMethodCalledOnWrongThreadViolation extends Violation {
+    /** @hide */
     public WebViewMethodCalledOnWrongThreadViolation(Throwable originStack) {
         super(null);
         setStackTrace(originStack.getStackTrace());
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 73fa01e..4c556ef 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -105,7 +104,10 @@
  *
  * @see Preference
  * @see PreferenceScreen
+ *
+ * @deprecated Use {@link android.support.v7.preference.PreferenceFragmentCompat}
  */
+@Deprecated
 public abstract class PreferenceFragment extends Fragment implements
         PreferenceManager.OnPreferenceTreeClickListener {
 
@@ -146,7 +148,11 @@
      * Interface that PreferenceFragment's containing activity should
      * implement to be able to process preference items that wish to
      * switch to a new fragment.
+     *
+     * @deprecated Use {@link
+     * android.support.v7.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback}
      */
+    @Deprecated
     public interface OnPreferenceStartFragmentCallback {
         /**
          * Called when the user has clicked on a Preference that has
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 13e1e26..32d68cd 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -81,6 +81,13 @@
     public static final String UNHIDE_CALL = "unhide";
 
     /**
+     * The method name used by the media scanner service to reload all localized ringtone titles due
+     * to a locale change.
+     * @hide
+     */
+    public static final String RETRANSLATE_CALL = "update_titles";
+
+    /**
      * This is for internal use by the media scanner only.
      * Name of the (optional) Uri parameter that determines whether to skip deleting
      * the file pointed to by the _data column, when deleting the database entry.
@@ -1358,6 +1365,18 @@
              * @hide
              */
             public static final String GENRE = "genre";
+
+            /**
+             * The resource URI of a localized title, if any
+             * <P>Type: TEXT</P>
+             * Conforms to this pattern:
+             *   Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
+             *   Authority: Package Name of ringtone title provider
+             *   First Path Segment: Type of resource (must be "string")
+             *   Second Path Segment: Resource ID of title
+             * @hide
+             */
+            public static final String TITLE_RESOURCE_URI = "title_resource_uri";
         }
 
         /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0a20c43..d4d24de 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -66,6 +66,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.speech.tts.TextToSpeech;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.AndroidException;
 import android.util.ArrayMap;
@@ -2113,6 +2114,9 @@
      * functions for accessing individual settings entries.
      */
     public static final class System extends NameValueTable {
+        // NOTE: If you add new settings here, be sure to add them to
+        // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoSystemSettingsLocked.
+
         private static final float DEFAULT_FONT_SCALE = 1.0f;
 
         /** @hide */
@@ -4562,6 +4566,9 @@
      * APIs for those values, not modified directly by applications.
      */
     public static final class Secure extends NameValueTable {
+        // NOTE: If you add new settings here, be sure to add them to
+        // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoSecureSettingsLocked.
+
         /**
          * The content:// style URL for this table
          */
@@ -5317,6 +5324,15 @@
         public static final String AUTOFILL_SERVICE = "autofill_service";
 
         /**
+         * Experimental autofill feature.
+         *
+         * <p>TODO(b/67867469): remove once feature is finished
+         * @hide
+         */
+        @TestApi
+        public static final String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
+
+        /**
          * @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
          */
         @Deprecated
@@ -7555,6 +7571,9 @@
      * explicitly modify through the system UI or specialized APIs for those values.
      */
     public static final class Global extends NameValueTable {
+        // NOTE: If you add new settings here, be sure to add them to
+        // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoGlobalSettingsLocked.
+
         /**
          * The content:// style URL for global secure settings items.  Not public.
          */
@@ -8513,6 +8532,13 @@
        public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
                "network_metered_multipath_preference";
 
+        /**
+         * Network watchlist last report time.
+         * @hide
+         */
+        public static final String NETWORK_WATCHLIST_LAST_REPORT_TIME =
+                "network_watchlist_last_report_time";
+
        /**
         * The thresholds of the wifi throughput badging (SD, HD etc.) as a comma-delimited list of
         * colon-delimited key-value pairs. The key is the badging enum value defined in
@@ -10132,8 +10158,12 @@
          * <p>
          * Type: int (0 for false, 1 for true)
          * @hide
+         * @deprecated Use {@link android.telephony.SubscriptionManager#ENHANCED_4G_MODE_ENABLED}
+         * instead.
          */
-        public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+        @Deprecated
+        public static final String ENHANCED_4G_MODE_ENABLED =
+                SubscriptionManager.ENHANCED_4G_MODE_ENABLED;
 
         /**
          * Whether VT (Video Telephony over IMS) is enabled
@@ -10141,8 +10171,10 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
+         * @deprecated Use {@link android.telephony.SubscriptionManager#VT_IMS_ENABLED} instead.
          */
-        public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+        @Deprecated
+        public static final String VT_IMS_ENABLED = SubscriptionManager.VT_IMS_ENABLED;
 
         /**
          * Whether WFC is enabled
@@ -10150,8 +10182,10 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
+         * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ENABLED} instead.
          */
-        public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+        @Deprecated
+        public static final String WFC_IMS_ENABLED = SubscriptionManager.WFC_IMS_ENABLED;
 
         /**
          * WFC mode on home/non-roaming network.
@@ -10159,8 +10193,10 @@
          * Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only
          *
          * @hide
+         * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_MODE} instead.
          */
-        public static final String WFC_IMS_MODE = "wfc_ims_mode";
+        @Deprecated
+        public static final String WFC_IMS_MODE = SubscriptionManager.WFC_IMS_MODE;
 
         /**
          * WFC mode on roaming network.
@@ -10168,8 +10204,11 @@
          * Type: int - see {@link #WFC_IMS_MODE} for values
          *
          * @hide
+         * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_MODE}
+         * instead.
          */
-        public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+        @Deprecated
+        public static final String WFC_IMS_ROAMING_MODE = SubscriptionManager.WFC_IMS_ROAMING_MODE;
 
         /**
          * Whether WFC roaming is enabled
@@ -10177,8 +10216,12 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
+         * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_ENABLED}
+         * instead
          */
-        public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+        @Deprecated
+        public static final String WFC_IMS_ROAMING_ENABLED =
+                SubscriptionManager.WFC_IMS_ROAMING_ENABLED;
 
         /**
          * Whether user can enable/disable LTE as a preferred network. A carrier might control
@@ -10859,7 +10902,7 @@
 
         /** User preferred subscriptions setting.
           * This holds the details of the user selected subscription from the card and
-          * the activation status. Each settings string have the coma separated values
+          * the activation status. Each settings string have the comma separated values
           * iccId,appType,appId,activationStatus,3gppIndex,3gpp2Index
           * @hide
          */
diff --git a/core/java/android/security/KeystoreArguments.aidl b/core/java/android/security/KeystoreArguments.aidl
index d636414..dc8ed50 100644
--- a/core/java/android/security/KeystoreArguments.aidl
+++ b/core/java/android/security/KeystoreArguments.aidl
@@ -17,4 +17,4 @@
 package android.security;
 
 /* @hide */
-parcelable KeystoreArguments;
+parcelable KeystoreArguments cpp_header "keystore/KeystoreArguments.h";
diff --git a/core/java/android/security/keymaster/ExportResult.aidl b/core/java/android/security/keymaster/ExportResult.aidl
index 4d9b2de..1748653 100644
--- a/core/java/android/security/keymaster/ExportResult.aidl
+++ b/core/java/android/security/keymaster/ExportResult.aidl
@@ -17,4 +17,4 @@
 package android.security.keymaster;
 
 /* @hide */
-parcelable ExportResult;
+parcelable ExportResult cpp_header "keystore/ExportResult.h";
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.aidl b/core/java/android/security/keymaster/KeyCharacteristics.aidl
index be739d3..32e75ad 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.aidl
+++ b/core/java/android/security/keymaster/KeyCharacteristics.aidl
@@ -17,4 +17,4 @@
 package android.security.keymaster;
 
 /* @hide */
-parcelable KeyCharacteristics;
+parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h";
diff --git a/core/java/android/security/keymaster/KeymasterArguments.aidl b/core/java/android/security/keymaster/KeymasterArguments.aidl
index 1a73206..44d9f09 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.aidl
+++ b/core/java/android/security/keymaster/KeymasterArguments.aidl
@@ -17,4 +17,4 @@
 package android.security.keymaster;
 
 /* @hide */
-parcelable KeymasterArguments;
+parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h";
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
index b7cd1c9..5c5db9e 100644
--- a/core/java/android/security/keymaster/KeymasterBlob.aidl
+++ b/core/java/android/security/keymaster/KeymasterBlob.aidl
@@ -17,4 +17,4 @@
 package android.security.keymaster;
 
 /* @hide */
-parcelable KeymasterBlob;
+parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h";
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
index dc1876a..ddb5cae 100644
--- a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
+++ b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
@@ -17,4 +17,4 @@
 package android.security.keymaster;
 
 /* @hide */
-parcelable KeymasterCertificateChain;
+parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h";
diff --git a/core/java/android/security/keymaster/OperationResult.aidl b/core/java/android/security/keymaster/OperationResult.aidl
index ed26c8d..db689d4 100644
--- a/core/java/android/security/keymaster/OperationResult.aidl
+++ b/core/java/android/security/keymaster/OperationResult.aidl
@@ -17,4 +17,4 @@
 package android.security.keymaster;
 
 /* @hide */
-parcelable OperationResult;
+parcelable OperationResult cpp_header "keystore/OperationResult.h";
diff --git a/core/java/android/service/autofill/FieldsDetection.java b/core/java/android/service/autofill/FieldsDetection.java
new file mode 100644
index 0000000..550ecf6
--- /dev/null
+++ b/core/java/android/service/autofill/FieldsDetection.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+/**
+ * Class 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):
+ *  - proper javadoc
+ *  - unhide / remove testApi
+ *  - add FieldsDetection management so service can set it just once and reference it in further
+ *    calls to improve performance (and also API to refresh it)
+ *  - rename to FieldsDetectionInfo or FieldClassification? (same for CTS tests)
+ *  - add FieldsDetectionUnitTest once API is well-defined
+ * @hide
+ */
+@TestApi
+public final class FieldsDetection implements Parcelable {
+
+    private final AutofillId mFieldId;
+    private final String mRemoteId;
+    private final String mValue;
+
+    /**
+     * Creates a field detection for just one field / value pair.
+     *
+     * @param fieldId autofill id of the field in the screen.
+     * @param remoteId id used by the service to identify the field later.
+     * @param value field value known to the service.
+     *
+     * TODO(b/67867469):
+     *  - proper javadoc
+     *  - change signature to allow more fields / values / match methods
+     *    - might also need to use a builder, where the constructor is the id for the fieldsdetector
+     *    - might need id for values as well
+     *  - add @NonNull / check it / add unit tests
+     *  - make 'value' input more generic so it can accept distance-based match and other matches
+     *  - throw exception if field value is less than X characters (somewhere between 7-10)
+     *  - make sure to limit total number of fields to around 10 or so
+     *  - use AutofillValue instead of String (so it can compare dates, for example)
+     */
+    public FieldsDetection(AutofillId fieldId, String remoteId, String value) {
+        mFieldId = fieldId;
+        mRemoteId = remoteId;
+        mValue = value;
+    }
+
+    /** @hide */
+    public AutofillId getFieldId() {
+        return mFieldId;
+    }
+
+    /** @hide */
+    public String getRemoteId() {
+        return mRemoteId;
+    }
+
+    /** @hide */
+    public String getValue() {
+        return mValue;
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        // Cannot disclose remoteId or value because they could contain PII
+        return new StringBuilder("FieldsDetection: [field=").append(mFieldId)
+                .append(", remoteId_length=").append(mRemoteId.length())
+                .append(", value_length=").append(mValue.length())
+                .append("]").toString();
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mFieldId, flags);
+        parcel.writeString(mRemoteId);
+        parcel.writeString(mValue);
+    }
+
+    public static final Parcelable.Creator<FieldsDetection> CREATOR =
+            new Parcelable.Creator<FieldsDetection>() {
+        @Override
+        public FieldsDetection createFromParcel(Parcel parcel) {
+            // TODO(b/67867469): remove comment below if it does not use a builder at the end
+            // Always go through the builder to ensure the data ingested by
+            // the system obeys the contract of the builder to avoid attacks
+            // using specially crafted parcels.
+            return new FieldsDetection(parcel.readParcelable(null), parcel.readString(),
+                    parcel.readString());
+        }
+
+        @Override
+        public FieldsDetection[] newArray(int size) {
+            return new FieldsDetection[size];
+        }
+    };
+}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index b1857b3..736d9ef 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -19,6 +19,7 @@
 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;
@@ -164,6 +165,10 @@
                         dest.writeStringList(event.mManuallyFilledDatasetIds.get(j));
                     }
                 }
+                dest.writeString(event.mDetectedRemoteId);
+                if (event.mDetectedRemoteId != null) {
+                    dest.writeInt(event.mDetectedFieldScore);
+                }
             }
         }
     }
@@ -226,6 +231,7 @@
          * <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 */
@@ -253,6 +259,9 @@
         @Nullable private final ArrayList<AutofillId> mManuallyFilledFieldIds;
         @Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds;
 
+        @Nullable private final String mDetectedRemoteId;
+        private final int mDetectedFieldScore;
+
         /**
          * Returns the type of the event.
          *
@@ -355,6 +364,39 @@
         }
 
         /**
+         * Gets the results of the last {@link FieldsDetection} request.
+         *
+         * @return map of edit-distance match ({@code 0} means full match,
+         * {@code 1} means 1 character different, etc...) by remote id (as set in the
+         * {@link FieldsDetection} constructor), or {@code null} if none of the user-input values
+         * matched the requested detection.
+         *
+         * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
+         * service requested {@link FillResponse.Builder#setFieldsDetection(FieldsDetection) fields
+         * detection}.
+         *
+         * TODO(b/67867469):
+         *  - improve javadoc
+         *  - refine score meaning (for example, should 1 be different of -1?)
+         *  - mention when it's set
+         *  - unhide
+         *  - unhide / remove testApi
+         *  - add @NonNull / check it / add unit tests
+         *
+         * @hide
+         */
+        @TestApi
+        @NonNull public Map<String, Integer> getDetectedFields() {
+            if (mDetectedRemoteId == null || mDetectedFieldScore == -1) {
+                return Collections.emptyMap();
+            }
+
+            final ArrayMap<String, Integer> map = new ArrayMap<>(1);
+            map.put(mDetectedRemoteId, mDetectedFieldScore);
+            return map;
+        }
+
+        /**
          * Returns which fields were available on datasets provided by the service but manually
          * entered by the user.
          *
@@ -430,7 +472,6 @@
          * and belonged to datasets.
          * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
          * respective entry on {@code manuallyFilledFieldIds}.
-         *
          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
          * {@code changedDatasetIds} doesn't match.
          * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
@@ -438,13 +479,15 @@
          *
          * @hide
          */
+        // TODO(b/67867469): document detection field parameters once stable
         public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
                 @Nullable List<String> selectedDatasetIds,
                 @Nullable ArraySet<String> ignoredDatasetIds,
                 @Nullable ArrayList<AutofillId> changedFieldIds,
                 @Nullable ArrayList<String> changedDatasetIds,
                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
-                @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds) {
+                @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
+                @Nullable String detectedRemoteId, int detectedFieldScore) {
             mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
                     "eventType");
             mDatasetId = datasetId;
@@ -467,6 +510,8 @@
             }
             mManuallyFilledFieldIds = manuallyFilledFieldIds;
             mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
+            mDetectedRemoteId = detectedRemoteId;
+            mDetectedFieldScore = detectedFieldScore;
         }
 
         @Override
@@ -479,6 +524,8 @@
                     + ", changedDatasetsIds=" + mChangedDatasetIds
                     + ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds
                     + ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds
+                    + ", detectedRemoteId=" + mDetectedRemoteId
+                    + ", detectedFieldScore=" + mDetectedFieldScore
                     + "]";
         }
     }
@@ -514,11 +561,15 @@
                         } else {
                             manuallyFilledDatasetIds = null;
                         }
+                        final String detectedRemoteId = parcel.readString();
+                        final int detectedFieldScore = detectedRemoteId == null ? -1
+                                : parcel.readInt();
 
                         selection.addEvent(new Event(eventType, datasetId, clientState,
                                 selectedDatasetIds, ignoredDatasets,
                                 changedFieldIds, changedDatasetIds,
-                                manuallyFilledFieldIds, manuallyFilledDatasetIds));
+                                manuallyFilledFieldIds, manuallyFilledDatasetIds,
+                                detectedRemoteId, detectedFieldScore));
                     }
                     return selection;
                 }
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 2f6342a..4e6a884 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -22,6 +22,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.Activity;
 import android.content.IntentSender;
 import android.content.pm.ParceledListSlice;
@@ -75,6 +76,7 @@
     private final @Nullable AutofillId[] mAuthenticationIds;
     private final @Nullable AutofillId[] mIgnoredIds;
     private final long mDisableDuration;
+    private final @Nullable FieldsDetection mFieldsDetection;
     private final int mFlags;
     private int mRequestId;
 
@@ -87,6 +89,7 @@
         mAuthenticationIds = builder.mAuthenticationIds;
         mIgnoredIds = builder.mIgnoredIds;
         mDisableDuration = builder.mDisableDuration;
+        mFieldsDetection = builder.mFieldsDetection;
         mFlags = builder.mFlags;
         mRequestId = INVALID_REQUEST_ID;
     }
@@ -132,6 +135,11 @@
     }
 
     /** @hide */
+    public @Nullable FieldsDetection getFieldsDetection() {
+        return mFieldsDetection;
+    }
+
+    /** @hide */
     public int getFlags() {
         return mFlags;
     }
@@ -167,6 +175,7 @@
         private AutofillId[] mAuthenticationIds;
         private AutofillId[] mIgnoredIds;
         private long mDisableDuration;
+        private FieldsDetection mFieldsDetection;
         private int mFlags;
         private boolean mDestroyed;
 
@@ -315,6 +324,25 @@
         }
 
         /**
+         * TODO(b/67867469):
+         *  - javadoc it
+         *  - javadoc how to check results
+         *  - unhide
+         *  - unhide / remove testApi
+         *  - throw exception (and document) if response has datasets or saveinfo
+         *  - throw exception (and document) if id on fieldsDetection is ignored
+         *
+         * @hide
+         */
+        @TestApi
+        public Builder setFieldsDetection(@NonNull FieldsDetection fieldsDetection) {
+            throwIfDestroyed();
+            throwIfDisableAutofillCalled();
+            mFieldsDetection = Preconditions.checkNotNull(fieldsDetection);
+            return this;
+        }
+
+        /**
          * Sets flags changing the response behavior.
          *
          * @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and
@@ -365,7 +393,8 @@
             if (duration <= 0) {
                 throw new IllegalArgumentException("duration must be greater than 0");
             }
-            if (mAuthentication != null || mDatasets != null || mSaveInfo != null) {
+            if (mAuthentication != null || mDatasets != null || mSaveInfo != null
+                    || mFieldsDetection != null) {
                 throw new IllegalStateException("disableAutofill() must be the only method called");
             }
 
@@ -388,11 +417,11 @@
          */
         public FillResponse build() {
             throwIfDestroyed();
-
             if (mAuthentication == null && mDatasets == null && mSaveInfo == null
-                    && mDisableDuration == 0) {
-                throw new IllegalStateException("need to provide at least one DataSet or a "
-                        + "SaveInfo or an authentication with a presentation or disable autofill");
+                    && mDisableDuration == 0 && mFieldsDetection == null) {
+                throw new IllegalStateException("need to provide: at least one DataSet, or a "
+                        + "SaveInfo, or an authentication with a presentation, "
+                        + "or a FieldsDetection, or disable autofill");
             }
             mDestroyed = true;
             return new FillResponse(this);
@@ -430,6 +459,7 @@
                 .append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
                 .append(", disableDuration=").append(mDisableDuration)
                 .append(", flags=").append(mFlags)
+                .append(", fieldDetection=").append(mFieldsDetection)
                 .append("]")
                 .toString();
     }
@@ -453,6 +483,7 @@
         parcel.writeParcelable(mPresentation, flags);
         parcel.writeParcelableArray(mIgnoredIds, flags);
         parcel.writeLong(mDisableDuration);
+        parcel.writeParcelable(mFieldsDetection, flags);
         parcel.writeInt(mFlags);
         parcel.writeInt(mRequestId);
     }
@@ -488,6 +519,10 @@
             if (disableDuration > 0) {
                 builder.disableAutofill(disableDuration);
             }
+            final FieldsDetection fieldsDetection = parcel.readParcelable(null);
+            if (fieldsDetection != null) {
+                builder.setFieldsDetection(fieldsDetection);
+            }
             builder.setFlags(parcel.readInt());
 
             final FillResponse response = builder.build();
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 0c2e4b7..cd233b8 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -97,6 +97,10 @@
     public static final String ACTION_RESOLVE_NO_PRIVILEGES =
             "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
 
+    /** Ask the user to input carrier confirmation code. */
+    public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
+            "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
+
     /** Intent extra set for resolution requests containing the package name of the calling app. */
     public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
             "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
@@ -105,6 +109,8 @@
     public static final int RESULT_OK = 0;
     /** Result code indicating that an active SIM must be deactivated to perform the operation. */
     public static final int RESULT_MUST_DEACTIVATE_SIM = -1;
+    /** Result code indicating that the user must input a carrier confirmation code. */
+    public static final int RESULT_NEED_CONFIRMATION_CODE = -2;
     // New predefined codes should have negative values.
 
     /** Start of implementation-specific error results. */
@@ -119,10 +125,13 @@
         RESOLUTION_ACTIONS = new ArraySet<>();
         RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
         RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
+        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE);
     }
 
     /** Boolean extra for resolution actions indicating whether the user granted consent. */
     public static final String RESOLUTION_EXTRA_CONSENT = "consent";
+    /** String extra for resolution actions indicating the carrier confirmation code. */
+    public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code";
 
     private final IEuiccService.Stub mStubWrapper;
 
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 3e992ec..080482b 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
+import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.Service;
 import android.content.ComponentName;
@@ -56,6 +57,8 @@
  *           &lt;/meta-data>
  * &lt;/service></pre>
  *
+ *  <p> Condition providers cannot be bound by the system on
+ * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
  */
 public abstract class ConditionProviderService extends Service {
     private final String TAG = ConditionProviderService.class.getSimpleName()
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 08d3118..dac663e7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -21,6 +21,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
@@ -82,6 +83,8 @@
  * method is the <i>only</i> one that is safe to call before {@link #onListenerConnected()}
  * or after {@link #onListenerDisconnected()}.
  * </p>
+ * <p> Notification listeners cannot get notification access or be bound by the system on
+ * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
  */
 public abstract class NotificationListenerService extends Service {
 
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 2b03ed6..cc4a0b6 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -340,6 +340,14 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    public static String formatDuration(long duration) {
+        synchronized (sFormatSync) {
+            int len = formatDurationLocked(duration, 0);
+            return new String(sFormatStr, 0, len);
+        }
+    }
+
+    /** @hide Just for debugging; not internationalized. */
     public static void formatDuration(long duration, PrintWriter pw) {
         formatDuration(duration, pw, 0);
     }
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 0b9552e..7412ef4 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -20,6 +20,7 @@
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.security.DigestException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
@@ -66,7 +67,7 @@
      */
     static ApkVerityResult generateApkVerity(RandomAccessFile apk,
             SignatureInfo signatureInfo, ByteBufferFactory bufferFactory)
-            throws IOException, SecurityException, NoSuchAlgorithmException {
+            throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
         assertSigningBlockAlignedAndHasFullPages(signatureInfo);
 
         long signingBlockSize =
@@ -112,6 +113,7 @@
         private final ByteBuffer mOutput;
 
         private final MessageDigest mMd;
+        private final byte[] mDigestBuffer = new byte[DIGEST_SIZE_BYTES];
         private final byte[] mSalt;
 
         private BufferedDigester(byte[] salt, ByteBuffer output) throws NoSuchAlgorithmException {
@@ -129,21 +131,22 @@
          * consumption will continuous from there.
          */
         @Override
-        public void consume(ByteBuffer buffer) {
+        public void consume(ByteBuffer buffer) throws DigestException {
             int offset = buffer.position();
             int remaining = buffer.remaining();
             while (remaining > 0) {
                 int allowance = (int) Math.min(remaining, BUFFER_SIZE - mBytesDigestedSinceReset);
-                mMd.update(slice(buffer, offset, offset + allowance));
+                // Optimization: set the buffer limit to avoid allocating a new ByteBuffer object.
+                buffer.limit(buffer.position() + allowance);
+                mMd.update(buffer);
                 offset += allowance;
                 remaining -= allowance;
                 mBytesDigestedSinceReset += allowance;
 
                 if (mBytesDigestedSinceReset == BUFFER_SIZE) {
-                    byte[] digest = mMd.digest();
-                    mOutput.put(digest);
-
-                    mMd.reset();
+                    mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
+                    mOutput.put(mDigestBuffer);
+                    // After digest, MessageDigest resets automatically, so no need to reset again.
                     mMd.update(mSalt);
                     mBytesDigestedSinceReset = 0;
                 }
@@ -152,12 +155,12 @@
 
         /** Finish the current digestion if any. */
         @Override
-        public void finish() {
+        public void finish() throws DigestException {
             if (mBytesDigestedSinceReset == 0) {
                 return;
             }
-            byte[] digest = mMd.digest();
-            mOutput.put(digest);
+            mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
+            mOutput.put(mDigestBuffer);
         }
 
         private void fillUpLastOutputChunk() {
@@ -174,7 +177,7 @@
      * digest the remaining.
      */
     private static void consumeByChunk(DataDigester digester, DataSource source, int chunkSize)
-            throws IOException {
+            throws IOException, DigestException {
         long inputRemaining = source.size();
         long inputOffset = 0;
         while (inputRemaining > 0) {
@@ -191,7 +194,7 @@
 
     private static void generateApkVerityDigestAtLeafLevel(RandomAccessFile apk,
             SignatureInfo signatureInfo, byte[] salt, ByteBuffer output)
-            throws IOException, NoSuchAlgorithmException {
+            throws IOException, NoSuchAlgorithmException, DigestException {
         BufferedDigester digester = new BufferedDigester(salt, output);
 
         // 1. Digest from the beginning of the file, until APK Signing Block is reached.
@@ -230,7 +233,7 @@
 
     private static byte[] generateApkVerityTree(RandomAccessFile apk, SignatureInfo signatureInfo,
             byte[] salt, int[] levelOffset, ByteBuffer output)
-            throws IOException, NoSuchAlgorithmException {
+            throws IOException, NoSuchAlgorithmException, DigestException {
         // 1. Digest the apk to generate the leaf level hashes.
         generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
                     levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
@@ -252,6 +255,7 @@
         byte[] rootHash = new byte[DIGEST_SIZE_BYTES];
         BufferedDigester digester = new BufferedDigester(salt, ByteBuffer.wrap(rootHash));
         digester.consume(slice(output, 0, CHUNK_SIZE_BYTES));
+        digester.finish();
         return rootHash;
     }
 
diff --git a/core/java/android/util/apk/ByteBufferDataSource.java b/core/java/android/util/apk/ByteBufferDataSource.java
index c2b9eff..3976568 100644
--- a/core/java/android/util/apk/ByteBufferDataSource.java
+++ b/core/java/android/util/apk/ByteBufferDataSource.java
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.security.DigestException;
 
 /**
  * {@link DataSource} which provides data from a {@link ByteBuffer}.
@@ -42,7 +43,7 @@
 
     @Override
     public void feedIntoDataDigester(DataDigester md, long offset, int size)
-            throws IOException {
+            throws IOException, DigestException {
         // There's no way to tell MessageDigest to read data from ByteBuffer from a position
         // other than the buffer's current position. We thus need to change the buffer's
         // position to match the requested offset.
diff --git a/core/java/android/util/apk/DataDigester.java b/core/java/android/util/apk/DataDigester.java
index 74dce7e..278be80 100644
--- a/core/java/android/util/apk/DataDigester.java
+++ b/core/java/android/util/apk/DataDigester.java
@@ -17,11 +17,12 @@
 package android.util.apk;
 
 import java.nio.ByteBuffer;
+import java.security.DigestException;
 
 interface DataDigester {
     /** Consumes the {@link ByteBuffer}. */
-    void consume(ByteBuffer buffer);
+    void consume(ByteBuffer buffer) throws DigestException;
 
     /** Finishes the digestion. Must be called after the last {@link #consume(ByteBuffer)}. */
-    void finish();
+    void finish() throws DigestException;
 }
diff --git a/core/java/android/util/apk/DataSource.java b/core/java/android/util/apk/DataSource.java
index 2b39f49..82f3800 100644
--- a/core/java/android/util/apk/DataSource.java
+++ b/core/java/android/util/apk/DataSource.java
@@ -17,6 +17,7 @@
 package android.util.apk;
 
 import java.io.IOException;
+import java.security.DigestException;
 
 /** Source of data to be digested. */
 interface DataSource {
@@ -32,5 +33,6 @@
      * @param offset offset of the region inside this data source.
      * @param size size (in bytes) of the region.
      */
-    void feedIntoDataDigester(DataDigester md, long offset, int size) throws IOException;
+    void feedIntoDataDigester(DataDigester md, long offset, int size)
+            throws IOException, DigestException;
 }
diff --git a/core/java/android/util/apk/MemoryMappedFileDataSource.java b/core/java/android/util/apk/MemoryMappedFileDataSource.java
index 04a1c6b..8d2b1e32 100644
--- a/core/java/android/util/apk/MemoryMappedFileDataSource.java
+++ b/core/java/android/util/apk/MemoryMappedFileDataSource.java
@@ -24,6 +24,7 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.DirectByteBuffer;
+import java.security.DigestException;
 
 /**
  * {@link DataSource} which provides data from a file descriptor by memory-mapping the sections
@@ -55,7 +56,7 @@
 
     @Override
     public void feedIntoDataDigester(DataDigester md, long offset, int size)
-            throws IOException {
+            throws IOException, DigestException {
         // IMPLEMENTATION NOTE: After a lot of experimentation, the implementation of this
         // method was settled on a straightforward mmap with prefaulting.
         //
diff --git a/core/java/android/view/DisplayFrames.java b/core/java/android/view/DisplayFrames.java
new file mode 100644
index 0000000..e6861d8
--- /dev/null
+++ b/core/java/android/view/DisplayFrames.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
+
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+
+/**
+ * Container class for all the display frames that affect how we do window layout on a display.
+ * @hide
+ */
+public class DisplayFrames {
+    public final int mDisplayId;
+
+    /**
+     * The current size of the screen; really; extends into the overscan area of the screen and
+     * doesn't account for any system elements like the status bar.
+     */
+    public final Rect mOverscan = new Rect();
+
+    /**
+     * The current visible size of the screen; really; (ir)regardless of whether the status bar can
+     * be hidden but not extending into the overscan area.
+     */
+    public final Rect mUnrestricted = new Rect();
+
+    /** Like mOverscan*, but allowed to move into the overscan region where appropriate. */
+    public final Rect mRestrictedOverscan = new Rect();
+
+    /**
+     * The current size of the screen; these may be different than (0,0)-(dw,dh) if the status bar
+     * can't be hidden; in that case it effectively carves out that area of the display from all
+     * other windows.
+     */
+    public final Rect mRestricted = new Rect();
+
+    /**
+     * During layout, the current screen borders accounting for any currently visible system UI
+     * elements.
+     */
+    public final Rect mSystem = new Rect();
+
+    /** For applications requesting stable content insets, these are them. */
+    public final Rect mStable = new Rect();
+
+    /**
+     * For applications requesting stable content insets but have also set the fullscreen window
+     * flag, these are the stable dimensions without the status bar.
+     */
+    public final Rect mStableFullscreen = new Rect();
+
+    /**
+     * During layout, the current screen borders with all outer decoration (status bar, input method
+     * dock) accounted for.
+     */
+    public final Rect mCurrent = new Rect();
+
+    /**
+     * During layout, the frame in which content should be displayed to the user, accounting for all
+     * screen decoration except for any space they deem as available for other content. This is
+     * usually the same as mCurrent*, but may be larger if the screen decor has supplied content
+     * insets.
+     */
+    public final Rect mContent = new Rect();
+
+    /**
+     * During layout, the frame in which voice content should be displayed to the user, accounting
+     * for all screen decoration except for any space they deem as available for other content.
+     */
+    public final Rect mVoiceContent = new Rect();
+
+    /** During layout, the current screen borders along which input method windows are placed. */
+    public final Rect mDock = new Rect();
+
+    private final Rect mDisplayInfoOverscan = new Rect();
+    private final Rect mRotatedDisplayInfoOverscan = new Rect();
+    public int mDisplayWidth;
+    public int mDisplayHeight;
+
+    public int mRotation;
+
+    public DisplayFrames(int displayId, DisplayInfo info) {
+        mDisplayId = displayId;
+        onDisplayInfoUpdated(info);
+    }
+
+    public void onDisplayInfoUpdated(DisplayInfo info) {
+        mDisplayWidth = info.logicalWidth;
+        mDisplayHeight = info.logicalHeight;
+        mRotation = info.rotation;
+        mDisplayInfoOverscan.set(
+                info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom);
+    }
+
+    public void onBeginLayout() {
+        switch (mRotation) {
+            case ROTATION_90:
+                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top;
+                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.right;
+                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.bottom;
+                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.left;
+                break;
+            case ROTATION_180:
+                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.right;
+                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.bottom;
+                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.left;
+                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.top;
+                break;
+            case ROTATION_270:
+                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.bottom;
+                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.left;
+                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.top;
+                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.right;
+                break;
+            default:
+                mRotatedDisplayInfoOverscan.set(mDisplayInfoOverscan);
+                break;
+        }
+
+        mRestrictedOverscan.set(0, 0, mDisplayWidth, mDisplayHeight);
+        mOverscan.set(mRestrictedOverscan);
+        mSystem.set(mRestrictedOverscan);
+        mUnrestricted.set(mRotatedDisplayInfoOverscan);
+        mUnrestricted.right = mDisplayWidth - mUnrestricted.right;
+        mUnrestricted.bottom = mDisplayHeight - mUnrestricted.bottom;
+        mRestricted.set(mUnrestricted);
+        mDock.set(mUnrestricted);
+        mContent.set(mUnrestricted);
+        mVoiceContent.set(mUnrestricted);
+        mStable.set(mUnrestricted);
+        mStableFullscreen.set(mUnrestricted);
+        mCurrent.set(mUnrestricted);
+
+    }
+
+    public int getInputMethodWindowVisibleHeight() {
+        return mDock.bottom - mCurrent.bottom;
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        mStable.writeToProto(proto, STABLE_BOUNDS);
+        proto.end(token);
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
+                + " r=" + mRotation);
+        final String myPrefix = prefix + "  ";
+        dumpFrame(mStable, "mStable", myPrefix, pw);
+        dumpFrame(mStableFullscreen, "mStableFullscreen", myPrefix, pw);
+        dumpFrame(mDock, "mDock", myPrefix, pw);
+        dumpFrame(mCurrent, "mCurrent", myPrefix, pw);
+        dumpFrame(mSystem, "mSystem", myPrefix, pw);
+        dumpFrame(mContent, "mContent", myPrefix, pw);
+        dumpFrame(mVoiceContent, "mVoiceContent", myPrefix, pw);
+        dumpFrame(mOverscan, "mOverscan", myPrefix, pw);
+        dumpFrame(mRestrictedOverscan, "mRestrictedOverscan", myPrefix, pw);
+        dumpFrame(mRestricted, "mRestricted", myPrefix, pw);
+        dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
+        dumpFrame(mDisplayInfoOverscan, "mDisplayInfoOverscan", myPrefix, pw);
+        dumpFrame(mRotatedDisplayInfoOverscan, "mRotatedDisplayInfoOverscan", myPrefix, pw);
+    }
+
+    private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
+        pw.print(prefix + name + "="); frame.printShortString(pw); pw.println();
+    }
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2166f6e..7c76bab 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -70,6 +70,7 @@
      * Name of the file that holds the shaders cache.
      */
     private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
+    private static final String CACHE_PATH_SKIASHADERS = "com.android.skia.shaders_cache";
 
     /**
      * System property used to enable or disable threaded rendering profiling.
@@ -272,7 +273,9 @@
      * @hide
      */
     public static void setupDiskCache(File cacheDir) {
-        ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
+        ThreadedRenderer.setupShadersDiskCache(
+                new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath(),
+                new File(cacheDir, CACHE_PATH_SKIASHADERS).getAbsolutePath());
     }
 
     /**
@@ -1007,7 +1010,7 @@
     /** Not actually public - internal use only. This doc to make lint happy */
     public static native void disableVsync();
 
-    static native void setupShadersDiskCache(String cacheFile);
+    static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
 
     private static native void nRotateProcessStatsBuffer();
     private static native void nSetProcessStatsBuffer(int fd);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c043dca..e12c0b0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14218,6 +14218,7 @@
      */
     public void setScaleX(float scaleX) {
         if (scaleX != getScaleX()) {
+            requireIsFinite(scaleX, "scaleX");
             invalidateViewProperty(true, false);
             mRenderNode.setScaleX(scaleX);
             invalidateViewProperty(false, true);
@@ -14254,6 +14255,7 @@
      */
     public void setScaleY(float scaleY) {
         if (scaleY != getScaleY()) {
+            requireIsFinite(scaleY, "scaleY");
             invalidateViewProperty(true, false);
             mRenderNode.setScaleY(scaleY);
             invalidateViewProperty(false, true);
@@ -14803,6 +14805,15 @@
         }
     }
 
+    private static void requireIsFinite(float transform, String propertyName) {
+        if (Float.isNaN(transform)) {
+            throw new IllegalArgumentException("Cannot set '" + propertyName + "' to Float.NaN");
+        }
+        if (Float.isInfinite(transform)) {
+            throw new IllegalArgumentException("Cannot set '" + propertyName + "' to infinity");
+        }
+    }
+
     /**
      * The visual x position of this view, in pixels. This is equivalent to the
      * {@link #setTranslationX(float) translationX} property plus the current
@@ -14889,6 +14900,7 @@
      */
     public void setElevation(float elevation) {
         if (elevation != getElevation()) {
+            requireIsFinite(elevation, "elevation");
             invalidateViewProperty(true, false);
             mRenderNode.setElevation(elevation);
             invalidateViewProperty(false, true);
@@ -14981,6 +14993,7 @@
      */
     public void setTranslationZ(float translationZ) {
         if (translationZ != getTranslationZ()) {
+            requireIsFinite(translationZ, "translationZ");
             invalidateViewProperty(true, false);
             mRenderNode.setTranslationZ(translationZ);
             invalidateViewProperty(false, true);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 37829f0..e30496f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -513,7 +513,7 @@
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
 
         if (!sCompatibilityDone) {
-            sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
+            sAlwaysAssignFocus = true;
 
             sCompatibilityDone = true;
         }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index c7e8dee..cca66d6 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -605,9 +605,10 @@
     public void setStoppedState(IBinder token, boolean stopped) {
         synchronized (mLock) {
             int count = mViews.size();
-            for (int i = 0; i < count; i++) {
+            for (int i = count - 1; i >= 0; i--) {
                 if (token == null || mParams.get(i).token == token) {
                     ViewRootImpl root = mRoots.get(i);
+                    // Client might remove the view by "stopped" event.
                     root.setWindowStopped(stopped);
                 }
             }
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 69cc100..cd1b190 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ClipData;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManagerInternal;
@@ -140,6 +141,30 @@
     }
 
     /**
+     * An interface to customize drag and drop behaviors.
+     */
+    public interface IDragDropCallback {
+        /**
+         * Called when drag operation is started.
+         */
+        default boolean performDrag(IWindow window, IBinder dragToken,
+                int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+                ClipData data) {
+            return true;
+        }
+
+        /**
+         * Called when drop result is reported.
+         */
+        default void reportDropResult(IWindow window, boolean consumed) {}
+
+        /**
+         * Called when drag operation is cancelled.
+         */
+        default void cancelDragAndDrop(IBinder dragToken) {}
+    }
+
+    /**
      * Request that the window manager call
      * {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
      * within a surface transaction at a later time.
@@ -225,9 +250,6 @@
      */
     public abstract boolean isKeyguardLocked();
 
-    /** @return {@code true} if the keyguard is going away. */
-    public abstract boolean isKeyguardGoingAway();
-
     /**
     * @return Whether the keyguard is showing and not occluded.
     */
@@ -354,4 +376,9 @@
      * {@param vr2dDisplayId}.
      */
     public abstract void setVr2dDisplayId(int vr2dDisplayId);
+
+    /**
+     * Sets callback to DragDropController.
+     */
+    public abstract void registerDragDropControllerCallback(IDragDropCallback callback);
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index ebe3633..534335b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -66,7 +66,6 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.app.ActivityManager.StackId;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo;
@@ -722,12 +721,6 @@
     public void setInitialDisplaySize(Display display, int width, int height, int density);
 
     /**
-     * Called by window manager to set the overscan region that should be used for the
-     * given display.
-     */
-    public void setDisplayOverscan(Display display, int left, int top, int right, int bottom);
-
-    /**
      * Check permissions when adding a window.
      *
      * @param attrs The window's LayoutParams.
@@ -1173,14 +1166,10 @@
     /**
      * Called when layout of the windows is about to start.
      *
-     * @param displayId Id of the display we are doing layout on.
-     * @param displayWidth The current full width of the screen.
-     * @param displayHeight The current full height of the screen.
-     * @param displayRotation The current rotation being applied to the base window.
+     * @param displayFrames frames of the display we are doing layout on.
      * @param uiMode The current uiMode in configuration.
      */
-    public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
-                              int displayRotation, int uiMode);
+    default void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {}
 
     /**
      * Returns the bottom-most layer of the system decor, above which no policy decor should
@@ -1189,37 +1178,28 @@
     public int getSystemDecorLayerLw();
 
     /**
-     * Return the rectangle of the screen that is available for applications to run in.
-     * This will be called immediately after {@link #beginLayoutLw}.
-     *
-     * @param r The rectangle to be filled with the boundaries available to applications.
-     */
-    public void getContentRectLw(Rect r);
-
-    /**
-     * Called for each window attached to the window manager as layout is
-     * proceeding.  The implementation of this function must take care of
-     * setting the window's frame, either here or in finishLayout().
+     * Called for each window attached to the window manager as layout is proceeding. The
+     * implementation of this function must take care of setting the window's frame, either here or
+     * in finishLayout().
      *
      * @param win The window being positioned.
      * @param attached For sub-windows, the window it is attached to; this
      *                 window will already have had layoutWindow() called on it
      *                 so you can use its Rect.  Otherwise null.
+     * @param displayFrames The display frames.
      */
-    public void layoutWindowLw(WindowState win, WindowState attached);
+    default void layoutWindowLw(
+            WindowState win, WindowState attached, DisplayFrames displayFrames) {}
 
 
     /**
-     * Return the insets for the areas covered by system windows. These values
-     * are computed on the most recent layout, so they are not guaranteed to
-     * be correct.
+     * Return the insets for the areas covered by system windows. These values are computed on the
+     * most recent layout, so they are not guaranteed to be correct.
      *
      * @param attrs The LayoutParams of the window.
      * @param taskBounds The bounds of the task this window is on or {@code null} if no task is
      *                   associated with the window.
-     * @param displayRotation Rotation of the display.
-     * @param displayWidth The width of the display.
-     * @param displayHeight The height of the display.
+     * @param displayFrames display frames.
      * @param outContentInsets The areas covered by system windows, expressed as positive insets.
      * @param outStableInsets The areas covered by stable system windows irrespective of their
      *                        current visibility. Expressed as positive insets.
@@ -1227,16 +1207,11 @@
      * @return Whether to always consume the navigation bar.
      *         See {@link #isNavBarForcedShownLw(WindowState)}.
      */
-    public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
-            int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
-            Rect outStableInsets, Rect outOutsets);
-
-    /**
-     * Called when layout of the windows is finished.  After this function has
-     * returned, all windows given to layoutWindow() <em>must</em> have had a
-     * frame assigned.
-     */
-    public void finishLayoutLw();
+    default boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
+            DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
+            Rect outOutsets) {
+        return false;
+    }
 
     /** Layout state may have changed (so another layout will be performed) */
     static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001;
@@ -1653,11 +1628,6 @@
     public void showGlobalActions();
 
     /**
-     * @return The current height of the input method window.
-     */
-    public int getInputMethodWindowVisibleHeightLw();
-
-    /**
      * Called when the current user changes. Guaranteed to be called before the broadcast
      * of the new user id is made to all listeners.
      *
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index e35bb92..aeb8489 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -133,27 +133,6 @@
     }
 
     /**
-     * Returns a {@link LinksInfo} that may be applied to the text to annotate it with links
-     * information.
-     *
-     * @param text the text to generate annotations for
-     * @param linkMask See {@link android.text.util.Linkify} for a list of linkMasks that may be
-     *      specified. Subclasses of this interface may specify additional linkMasks
-     * @param defaultLocales  ordered list of locale preferences that can be used to disambiguate
-     *      the provided text. If no locale preferences exist, set this to null or an empty locale
-     *      list in which case the classifier will decide whether to use no locale information, use
-     *      a default locale, or use the system default.
-     *
-     * @throws IllegalArgumentException if text is null
-     * @hide
-     */
-    @WorkerThread
-    default LinksInfo getLinks(
-            @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) {
-        return LinksInfo.NO_OP;
-    }
-
-    /**
      * Returns a {@link TextLinks} that may be applied to the text to annotate it with links
      * information.
      *
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 9c4fd57..2ad6e02 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -30,13 +30,8 @@
 import android.provider.Browser;
 import android.provider.ContactsContract;
 import android.provider.Settings;
-import android.text.Spannable;
-import android.text.TextUtils;
-import android.text.method.WordIterator;
-import android.text.style.ClickableSpan;
 import android.text.util.Linkify;
 import android.util.Patterns;
-import android.view.View;
 import android.widget.TextViewMetrics;
 
 import com.android.internal.annotations.GuardedBy;
@@ -46,13 +41,8 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.text.BreakIterator;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -194,21 +184,6 @@
     }
 
     @Override
-    public LinksInfo getLinks(
-            @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) {
-        Preconditions.checkArgument(text != null);
-        try {
-            return LinksInfoFactory.create(
-                    mContext, getSmartSelection(defaultLocales), text.toString(), linkMask);
-        } catch (Throwable t) {
-            // Avoid throwing from this method. Log the error.
-            Log.e(LOG_TAG, "Error getting links info.", t);
-        }
-        // Getting here means something went wrong, return a NO_OP result.
-        return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
-    }
-
-    @Override
     public TextLinks generateLinks(
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
         Preconditions.checkNotNull(text);
@@ -260,7 +235,9 @@
             if (mSmartSelection == null || !Objects.equals(mLocale, locale)) {
                 destroySmartSelectionIfExistsLocked();
                 final ParcelFileDescriptor fd = getFdLocked(locale);
-                mSmartSelection = new SmartSelection(fd.getFd());
+                final int modelFd = fd.getFd();
+                mVersion = SmartSelection.getVersion(modelFd);
+                mSmartSelection = new SmartSelection(modelFd);
                 closeAndLogError(fd);
                 mLocale = locale;
             }
@@ -281,18 +258,26 @@
     @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
     private ParcelFileDescriptor getFdLocked(Locale locale) throws FileNotFoundException {
         ParcelFileDescriptor updateFd;
+        int updateVersion = -1;
         try {
             updateFd = ParcelFileDescriptor.open(
                     new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
+            if (updateFd != null) {
+                updateVersion = SmartSelection.getVersion(updateFd.getFd());
+            }
         } catch (FileNotFoundException e) {
             updateFd = null;
         }
         ParcelFileDescriptor factoryFd;
+        int factoryVersion = -1;
         try {
             final String factoryModelFilePath = getFactoryModelFilePathsLocked().get(locale);
             if (factoryModelFilePath != null) {
                 factoryFd = ParcelFileDescriptor.open(
                         new File(factoryModelFilePath), ParcelFileDescriptor.MODE_READ_ONLY);
+                if (factoryFd != null) {
+                    factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
+                }
             } else {
                 factoryFd = null;
             }
@@ -328,15 +313,11 @@
             return factoryFd;
         }
 
-        final int updateVersion = SmartSelection.getVersion(updateFdInt);
-        final int factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
         if (updateVersion > factoryVersion) {
             closeAndLogError(factoryFd);
-            mVersion = updateVersion;
             return updateFd;
         } else {
             closeAndLogError(updateFd);
-            mVersion = factoryVersion;
             return factoryFd;
         }
     }
@@ -516,180 +497,6 @@
     }
 
     /**
-     * Detects and creates links for specified text.
-     */
-    private static final class LinksInfoFactory {
-
-        private LinksInfoFactory() {}
-
-        public static LinksInfo create(
-                Context context, SmartSelection smartSelection, String text, int linkMask) {
-            final WordIterator wordIterator = new WordIterator();
-            wordIterator.setCharSequence(text, 0, text.length());
-            final List<SpanSpec> spans = new ArrayList<>();
-            int start = 0;
-            int end;
-            while ((end = wordIterator.nextBoundary(start)) != BreakIterator.DONE) {
-                final String token = text.substring(start, end);
-                if (TextUtils.isEmpty(token)) {
-                    continue;
-                }
-
-                final int[] selection = smartSelection.suggest(text, start, end);
-                final int selectionStart = selection[0];
-                final int selectionEnd = selection[1];
-                if (selectionStart >= 0 && selectionEnd <= text.length()
-                        && selectionStart <= selectionEnd) {
-                    final SmartSelection.ClassificationResult[] results =
-                            smartSelection.classifyText(
-                                    text, selectionStart, selectionEnd,
-                                    getHintFlags(text, selectionStart, selectionEnd));
-                    if (results.length > 0) {
-                        final String type = getHighestScoringType(results);
-                        if (matches(type, linkMask)) {
-                            // For links without disambiguation, we simply use the default intent.
-                            final List<Intent> intents = IntentFactory.create(
-                                    context, type, text.substring(selectionStart, selectionEnd));
-                            if (!intents.isEmpty() && hasActivityHandler(context, intents.get(0))) {
-                                final ClickableSpan span = createSpan(context, intents.get(0));
-                                spans.add(new SpanSpec(selectionStart, selectionEnd, span));
-                            }
-                        }
-                    }
-                }
-                start = end;
-            }
-            return new LinksInfoImpl(text, avoidOverlaps(spans, text));
-        }
-
-        /**
-         * Returns true if the classification type matches the specified linkMask.
-         */
-        private static boolean matches(String type, int linkMask) {
-            type = type.trim().toLowerCase(Locale.ENGLISH);
-            if ((linkMask & Linkify.PHONE_NUMBERS) != 0
-                    && TextClassifier.TYPE_PHONE.equals(type)) {
-                return true;
-            }
-            if ((linkMask & Linkify.EMAIL_ADDRESSES) != 0
-                    && TextClassifier.TYPE_EMAIL.equals(type)) {
-                return true;
-            }
-            if ((linkMask & Linkify.MAP_ADDRESSES) != 0
-                    && TextClassifier.TYPE_ADDRESS.equals(type)) {
-                return true;
-            }
-            if ((linkMask & Linkify.WEB_URLS) != 0
-                    && TextClassifier.TYPE_URL.equals(type)) {
-                return true;
-            }
-            return false;
-        }
-
-        /**
-         * Trim the number of spans so that no two spans overlap.
-         *
-         * This algorithm first ensures that there is only one span per start index, then it
-         * makes sure that no two spans overlap.
-         */
-        private static List<SpanSpec> avoidOverlaps(List<SpanSpec> spans, String text) {
-            Collections.sort(spans, Comparator.comparingInt(span -> span.mStart));
-            // Group spans by start index. Take the longest span.
-            final Map<Integer, SpanSpec> reps = new LinkedHashMap<>();  // order matters.
-            final int size = spans.size();
-            for (int i = 0; i < size; i++) {
-                final SpanSpec span = spans.get(i);
-                final LinksInfoFactory.SpanSpec rep = reps.get(span.mStart);
-                if (rep == null || rep.mEnd < span.mEnd) {
-                    reps.put(span.mStart, span);
-                }
-            }
-            // Avoid span intersections. Take the longer span.
-            final LinkedList<SpanSpec> result = new LinkedList<>();
-            for (SpanSpec rep : reps.values()) {
-                if (result.isEmpty()) {
-                    result.add(rep);
-                    continue;
-                }
-
-                final SpanSpec last = result.getLast();
-                if (rep.mStart < last.mEnd) {
-                    // Spans intersect. Use the one with characters.
-                    if ((rep.mEnd - rep.mStart) > (last.mEnd - last.mStart)) {
-                        result.set(result.size() - 1, rep);
-                    }
-                } else {
-                    result.add(rep);
-                }
-            }
-            return result;
-        }
-
-        private static ClickableSpan createSpan(final Context context, final Intent intent) {
-            return new ClickableSpan() {
-                // TODO: Style this span.
-                @Override
-                public void onClick(View widget) {
-                    context.startActivity(intent);
-                }
-            };
-        }
-
-        private static boolean hasActivityHandler(Context context, Intent intent) {
-            if (intent == null) {
-                return false;
-            }
-            final ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
-            return resolveInfo != null && resolveInfo.activityInfo != null;
-        }
-
-        /**
-         * Implementation of LinksInfo that adds ClickableSpans to the specified text.
-         */
-        private static final class LinksInfoImpl implements LinksInfo {
-
-            private final CharSequence mOriginalText;
-            private final List<SpanSpec> mSpans;
-
-            LinksInfoImpl(CharSequence originalText, List<SpanSpec> spans) {
-                mOriginalText = originalText;
-                mSpans = spans;
-            }
-
-            @Override
-            public boolean apply(@NonNull CharSequence text) {
-                Preconditions.checkArgument(text != null);
-                if (text instanceof Spannable && mOriginalText.toString().equals(text.toString())) {
-                    Spannable spannable = (Spannable) text;
-                    final int size = mSpans.size();
-                    for (int i = 0; i < size; i++) {
-                        final SpanSpec span = mSpans.get(i);
-                        spannable.setSpan(span.mSpan, span.mStart, span.mEnd, 0);
-                    }
-                    return true;
-                }
-                return false;
-            }
-        }
-
-        /**
-         * Span plus its start and end index.
-         */
-        private static final class SpanSpec {
-
-            private final int mStart;
-            private final int mEnd;
-            private final ClickableSpan mSpan;
-
-            SpanSpec(int start, int end, ClickableSpan span) {
-                mStart = start;
-                mEnd = end;
-                mSpan = span;
-            }
-        }
-    }
-
-    /**
      * Creates intents based on the classification type.
      */
     private static final class IntentFactory {
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 83af19b..fbf6535 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -48,9 +48,13 @@
     private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
     private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
     private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
-    private static final int VERSION_TAG = MetricsEvent.FIELD_SELECTION_VERSION_TAG;
-    private static final int SMART_INDICES = MetricsEvent.FIELD_SELECTION_SMART_RANGE;
-    private static final int EVENT_INDICES = MetricsEvent.FIELD_SELECTION_RANGE;
+    private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+    private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
+    private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+    private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
+    private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
+    private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
+    private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
     private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
 
     private static final String ZERO = "0";
@@ -83,7 +87,7 @@
     private long mSessionStartTime;
     private long mLastEventTime;
     private boolean mSmartSelectionTriggered;
-    private String mVersionTag;
+    private String mModelName;
 
     public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
         mWidgetType = widgetType;
@@ -115,7 +119,7 @@
             case SelectionEvent.EventType.SMART_SELECTION_SINGLE:  // fall through
             case SelectionEvent.EventType.SMART_SELECTION_MULTI:
                 mSmartSelectionTriggered = true;
-                mVersionTag = getVersionTag(event);
+                mModelName = getModelName(event);
                 mSmartIndices[0] = event.mStart;
                 mSmartIndices[1] = event.mEnd;
                 break;
@@ -137,14 +141,18 @@
         final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime;
         final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
                 .setType(getLogType(event))
-                .setSubtype(getLogSubType(event))
+                .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
                 .setPackageName(mContext.getPackageName())
                 .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime)
                 .addTaggedData(PREV_EVENT_DELTA, prevEventDelta)
                 .addTaggedData(INDEX, mIndex)
-                .addTaggedData(VERSION_TAG, mVersionTag)
-                .addTaggedData(SMART_INDICES, getSmartDelta())
-                .addTaggedData(EVENT_INDICES, getEventDelta(event))
+                .addTaggedData(WIDGET_TYPE, getWidgetTypeName())
+                .addTaggedData(MODEL_NAME, mModelName)
+                .addTaggedData(ENTITY_TYPE, event.mEntityType)
+                .addTaggedData(SMART_START, getSmartRangeDelta(mSmartIndices[0]))
+                .addTaggedData(SMART_END, getSmartRangeDelta(mSmartIndices[1]))
+                .addTaggedData(EVENT_START, getRangeDelta(event.mStart))
+                .addTaggedData(EVENT_END, getRangeDelta(event.mEnd))
                 .addTaggedData(SESSION_ID, mSessionId);
         mMetricsLogger.write(log);
         debugLog(log);
@@ -169,7 +177,7 @@
         mSessionStartTime = 0;
         mLastEventTime = 0;
         mSmartSelectionTriggered = false;
-        mVersionTag = getVersionTag(null);
+        mModelName = getModelName(null);
         mSessionId = null;
     }
 
@@ -251,113 +259,64 @@
         }
     }
 
-    private static int getLogSubType(SelectionEvent event) {
-        switch (event.mEntityType) {
-            case TextClassifier.TYPE_OTHER:
-                return MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER;
-            case TextClassifier.TYPE_EMAIL:
-                return MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL;
-            case TextClassifier.TYPE_PHONE:
-                return MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE;
-            case TextClassifier.TYPE_ADDRESS:
-                return MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS;
-            case TextClassifier.TYPE_URL:
-                return MetricsEvent.TEXT_CLASSIFIER_TYPE_URL;
-            default:
-                return MetricsEvent.TEXT_CLASSIFIER_TYPE_UNKNOWN;
-        }
+    private int getRangeDelta(int offset) {
+        return offset - mOrigStart;
     }
 
-    private static String getLogSubTypeString(int logSubType) {
-        switch (logSubType) {
-            case MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER:
-                return TextClassifier.TYPE_OTHER;
-            case MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL:
-                return TextClassifier.TYPE_EMAIL;
-            case MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE:
-                return TextClassifier.TYPE_PHONE;
-            case MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS:
-                return TextClassifier.TYPE_ADDRESS;
-            case MetricsEvent.TEXT_CLASSIFIER_TYPE_URL:
-                return TextClassifier.TYPE_URL;
-            default:
-                return TextClassifier.TYPE_UNKNOWN;
-        }
+    private int getSmartRangeDelta(int offset) {
+        return mSmartSelectionTriggered ? getRangeDelta(offset) : 0;
     }
 
-    private int getSmartDelta() {
-        if (mSmartSelectionTriggered) {
-            return (clamp(mSmartIndices[0] - mOrigStart) << 16)
-                    | (clamp(mSmartIndices[1] - mOrigStart) & 0xffff);
-        }
-        // If the smart selection model was not run, return invalid selection indices [0,0]. This
-        // allows us to tell from the terminal event alone whether the model was run.
-        return 0;
-    }
-
-    private int getEventDelta(SelectionEvent event) {
-        return (clamp(event.mStart - mOrigStart) << 16)
-                | (clamp(event.mEnd - mOrigStart) & 0xffff);
-    }
-
-    private String getVersionTag(@Nullable SelectionEvent event) {
-        final String widgetType;
+    private String getWidgetTypeName() {
         switch (mWidgetType) {
             case WidgetType.TEXTVIEW:
-                widgetType = TEXTVIEW;
-                break;
+                return TEXTVIEW;
             case WidgetType.WEBVIEW:
-                widgetType = WEBVIEW;
-                break;
+                return WEBVIEW;
             case WidgetType.EDITTEXT:
-                widgetType = EDITTEXT;
-                break;
+                return EDITTEXT;
             case WidgetType.EDIT_WEBVIEW:
-                widgetType = EDIT_WEBVIEW;
-                break;
+                return EDIT_WEBVIEW;
             default:
-                widgetType = UNKNOWN;
+                return UNKNOWN;
         }
-        final String version = event == null
+    }
+
+    private String getModelName(@Nullable SelectionEvent event) {
+        return event == null
                 ? SelectionEvent.NO_VERSION_TAG
                 : Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
-        return String.format("%s/%s", widgetType, version);
     }
 
     private static String createSessionId() {
         return UUID.randomUUID().toString();
     }
 
-    private static int clamp(int val) {
-        return Math.max(Math.min(val, Short.MAX_VALUE), Short.MIN_VALUE);
-    }
-
     private static void debugLog(LogMaker log) {
         if (!DEBUG_LOG_ENABLED) return;
 
-        final String tag = Objects.toString(log.getTaggedData(VERSION_TAG), "tag");
+        final String widget = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
         final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
         if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
             String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
             sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
-            Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId));
+            Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
         }
 
+        final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
+        final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
         final String type = getLogTypeString(log.getType());
-        final String subType = getLogSubTypeString(log.getSubtype());
+        final int smartStart = Integer.parseInt(
+                Objects.toString(log.getTaggedData(SMART_START), ZERO));
+        final int smartEnd = Integer.parseInt(
+                Objects.toString(log.getTaggedData(SMART_END), ZERO));
+        final int eventStart = Integer.parseInt(
+                Objects.toString(log.getTaggedData(EVENT_START), ZERO));
+        final int eventEnd = Integer.parseInt(
+                Objects.toString(log.getTaggedData(EVENT_END), ZERO));
 
-        final int smartIndices = Integer.parseInt(
-                Objects.toString(log.getTaggedData(SMART_INDICES), ZERO));
-        final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16);
-        final int smartEnd = (short) (smartIndices & 0xffff);
-
-        final int eventIndices = Integer.parseInt(
-                Objects.toString(log.getTaggedData(EVENT_INDICES), ZERO));
-        final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16);
-        final int eventEnd = (short) (eventIndices & 0xffff);
-
-        Log.d(LOG_TAG, String.format("%2d: %s/%s, context=%d,%d - old=%d,%d (%s)",
-                index, type, subType, eventStart, eventEnd, smartStart, smartEnd, tag));
+        Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+                index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
     }
 
     /**
@@ -369,12 +328,12 @@
         /**
          * Use this to specify an indeterminate positive index.
          */
-        public static final int OUT_OF_BOUNDS = Short.MAX_VALUE;
+        public static final int OUT_OF_BOUNDS = Integer.MAX_VALUE;
 
         /**
          * Use this to specify an indeterminate negative index.
          */
-        public static final int OUT_OF_BOUNDS_NEGATIVE = Short.MIN_VALUE;
+        public static final int OUT_OF_BOUNDS_NEGATIVE = Integer.MIN_VALUE;
 
         private static final String NO_VERSION_TAG = "";
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9295f5c..665d694 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -308,10 +308,15 @@
  * WebView may upload anonymous diagnostic data to Google when the user has consented. This data
  * helps Google improve WebView. Data is collected on a per-app basis for each app which has
  * instantiated a WebView. An individual app can opt out of this feature by putting the following
- * tag in its manifest:
+ * tag in its manifest's {@code <application>} element:
  * <pre>
- * &lt;meta-data android:name="android.webkit.WebView.MetricsOptOut"
- *            android:value="true" /&gt;
+ * &lt;manifest&gt;
+ *     &lt;application&gt;
+ *         ...
+ *         &lt;meta-data android:name=&quot;android.webkit.WebView.MetricsOptOut&quot;
+ *             android:value=&quot;true&quot; /&gt;
+ *     &lt;/application&gt;
+ * &lt;/manifest&gt;
  * </pre>
  * <p>
  * Data will only be uploaded for a given app if the user has consented AND the app has not opted
@@ -323,11 +328,17 @@
  * If Safe Browsing is enabled, WebView will block malicious URLs and present a warning UI to the
  * user to allow them to navigate back safely or proceed to the malicious page.
  * <p>
- * The recommended way for apps to enable the feature is putting the following tag in the manifest:
+ * The recommended way for apps to enable the feature is putting the following tag in the manifest's
+ * {@code <application>} element:
  * <p>
  * <pre>
- * &lt;meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
- *            android:value="true" /&gt;
+ * &lt;manifest&gt;
+ *     &lt;application&gt;
+ *         ...
+ *         &lt;meta-data android:name=&quot;android.webkit.WebView.EnableSafeBrowsing&quot;
+ *             android:value=&quot;true&quot; /&gt;
+ *     &lt;/application&gt;
+ * &lt;/manifest&gt;
  * </pre>
  *
  */
diff --git a/core/java/android/webkit/WebViewFragment.java b/core/java/android/webkit/WebViewFragment.java
index d803f62d..e5b7c8d 100644
--- a/core/java/android/webkit/WebViewFragment.java
+++ b/core/java/android/webkit/WebViewFragment.java
@@ -27,7 +27,10 @@
  * A fragment that displays a WebView.
  * <p>
  * The WebView is automically paused or resumed when the Fragment is paused or resumed.
+ *
+ * @deprecated Manually call {@link WebView#onPause()} and {@link WebView#onResume()}
  */
+@Deprecated
 public class WebViewFragment extends Fragment {
     private WebView mWebView;
     private boolean mIsWebViewAvailable;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 57aa119..d477ffd 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6553,12 +6553,12 @@
          * Adds "PROCESS_TEXT" menu items to the specified menu.
          */
         public void onInitializeMenu(Menu menu) {
-            final int size = mSupportedActivities.size();
             loadSupportedActivities();
+            final int size = mSupportedActivities.size();
             for (int i = 0; i < size; i++) {
                 final ResolveInfo resolveInfo = mSupportedActivities.get(i);
                 menu.add(Menu.NONE, Menu.NONE,
-                        Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i++,
+                        Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
                         getLabel(resolveInfo))
                         .setIntent(createProcessTextIntentForResolveInfo(resolveInfo))
                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 8dc8cab..e91db13 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1354,6 +1354,7 @@
         }
 
         mDecorView = createDecorView(mBackgroundView);
+        mDecorView.setIsRootNamespace(true);
 
         // The background owner should be elevated so that it casts a shadow.
         mBackgroundView.setElevation(mElevation);
diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
similarity index 98%
rename from core/java/com/android/internal/app/NightDisplayController.java
rename to core/java/com/android/internal/app/ColorDisplayController.java
index b8bfc64..b8682a8 100644
--- a/core/java/com/android/internal/app/NightDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -42,14 +42,14 @@
 import java.time.format.DateTimeParseException;
 
 /**
- * Controller for managing Night display settings.
+ * Controller for managing night display and color mode settings.
  * <p/>
  * Night display tints your screen red at night. This makes it easier to look at your screen in
  * dim light and may help you fall asleep more easily.
  */
-public final class NightDisplayController {
+public final class ColorDisplayController {
 
-    private static final String TAG = "NightDisplayController";
+    private static final String TAG = "ColorDisplayController";
     private static final boolean DEBUG = false;
 
     @Retention(RetentionPolicy.SOURCE)
@@ -114,11 +114,11 @@
 
     private Callback mCallback;
 
-    public NightDisplayController(@NonNull Context context) {
+    public ColorDisplayController(@NonNull Context context) {
         this(context, ActivityManager.getCurrentUser());
     }
 
-    public NightDisplayController(@NonNull Context context, int userId) {
+    public ColorDisplayController(@NonNull Context context, int userId) {
         mContext = context.getApplicationContext();
         mUserId = userId;
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
similarity index 72%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
copy to core/java/com/android/internal/net/INetworkWatchlistManager.aidl
index 8cf3be8..7e88369 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
+++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shared.recents.model;
+package com.android.internal.net;
 
-import android.view.MotionEvent;
+import android.os.SharedMemory;
 
-oneway interface IOverviewProxy {
-    void onMotionEvent(in MotionEvent event);
+/** {@hide} */
+interface INetworkWatchlistManager {
+    boolean startWatchlistLogging();
+    boolean stopWatchlistLogging();
+    void reportWatchlistIfNecessary();
 }
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 3d3e148..5eda81b 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -43,6 +43,8 @@
 /**
  * Creates {@link NetworkStats} instances by parsing various {@code /proc/}
  * files as needed.
+ *
+ * @hide
  */
 public class NetworkStatsFactory {
     private static final String TAG = "NetworkStatsFactory";
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2ec64a5..f2483c0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3639,6 +3639,7 @@
 
     public void addIsolatedUidLocked(int isolatedUid, int appUid) {
         mIsolatedUids.put(isolatedUid, appUid);
+        StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1);
     }
 
     /**
@@ -3659,9 +3660,11 @@
      * @see #scheduleRemoveIsolatedUidLocked(int, int)
      */
     public void removeIsolatedUidLocked(int isolatedUid) {
-        mIsolatedUids.delete(isolatedUid);
-        mKernelUidCpuTimeReader.removeUid(isolatedUid);
-        mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
+      StatsLog.write(
+          StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0);
+      mIsolatedUids.delete(isolatedUid);
+      mKernelUidCpuTimeReader.removeUid(isolatedUid);
+      mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
     }
 
     public int mapUid(int uid) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index fb8b9f7..3552e43 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -28,10 +28,6 @@
         "-Wunused",
         "-Wunreachable-code",
 
-        // necessary for Clang as the GL bindings need to turn
-        // off a GCC warning that Clang doesn't know.
-        "-Wno-unknown-pragmas",
-
         // TODO: Linear blending should be enabled by default, but we are
         // TODO: making it an opt-in while it's a work in progress
         //"-DANDROID_ENABLE_LINEAR_BLENDING",
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 6163588..a9d75fd 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -17,7 +17,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index df1aa20..75a25fe 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -17,7 +17,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index 6d4f6ec..ee5b594 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index e630cfca..da7d0f0 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index ab9cbb1..391ae53 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 8f71a6d..09dce32 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index f83d204..99922cf 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES2/gl2.h>
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index b649daf..adc635e 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES3/gl3.h>
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 07d920f..512f562 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -17,7 +17,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <stdint.h>
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 723dd4c..5543fca 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -17,7 +17,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES3/gl31.h>
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 62a6e6c..2f1e31e 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -17,7 +17,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <stdint.h>
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 57263b1..870a0c2 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -54,6 +54,7 @@
 #include <renderthread/RenderProxy.h>
 #include <renderthread/RenderTask.h>
 #include <renderthread/RenderThread.h>
+#include <pipeline/skia/ShaderCache.h>
 
 namespace android {
 
@@ -970,10 +971,14 @@
 // ----------------------------------------------------------------------------
 
 static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
-        jstring diskCachePath) {
+        jstring diskCachePath, jstring skiaDiskCachePath) {
     const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
     android::egl_set_cache_filename(cacheArray);
     env->ReleaseStringUTFChars(diskCachePath, cacheArray);
+
+    const char* skiaCacheArray = env->GetStringUTFChars(skiaDiskCachePath, NULL);
+    uirenderer::skiapipeline::ShaderCache::get().setFilename(skiaCacheArray);
+    env->ReleaseStringUTFChars(skiaDiskCachePath, skiaCacheArray);
 }
 
 // ----------------------------------------------------------------------------
@@ -1018,7 +1023,7 @@
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
     { "nSerializeDisplayListTree", "(J)V", (void*) android_view_ThreadedRenderer_serializeDisplayListTree },
     { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
-    { "setupShadersDiskCache", "(Ljava/lang/String;)V",
+    { "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V",
                 (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
     { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
     { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index ac23eca..40ff7e4 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp
index 4996bc8..3b18f2b 100644
--- a/core/jni/hwbinder/EphemeralStorage.cpp
+++ b/core/jni/hwbinder/EphemeralStorage.cpp
@@ -111,6 +111,7 @@
                 break;                                                         \
             }
 
+__attribute__((no_sanitize("unsigned-integer-overflow")))
 void EphemeralStorage::release(JNIEnv *env) {
     for (size_t i = mItems.size(); i--;) {
         const Item &item = mItems[i];
diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto
new file mode 100644
index 0000000..a95fa57
--- /dev/null
+++ b/core/proto/android/os/cpuinfo.proto
@@ -0,0 +1,110 @@
+/*
+ * 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";
+
+option java_multiple_files = true;
+option java_outer_classname = "CpuInfoProto";
+
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
+package android.os;
+
+/**
+ * Data structure of the linux command
+ * 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
+ *
+ * Next Tag: 6
+ */
+message CpuInfo {
+
+    message TaskStats {
+        option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+        optional int32 total = 1;    // total number of cpu tasks
+        optional int32 running = 2;  // number of running tasks
+        optional int32 sleeping = 3; // number of sleeping tasks
+        optional int32 stopped = 4;  // number of stopped tasks
+        optional int32 zombie = 5;   // number of zombie tasks
+    }
+    optional TaskStats task_stats = 1;
+
+    message MemStats { // unit in kB
+        option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+        optional int32 total = 1;
+        optional int32 used = 2;
+        optional int32 free = 3;
+        optional int32 buffers = 4;
+        optional int32 cached = 5;
+    }
+    optional MemStats mem = 2;
+    optional MemStats swap = 3;
+
+    message CpuUsage { // unit is percentage %
+        option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+        optional int32 cpu = 1;   // 400% cpu indicates 4 cores
+        optional int32 user = 2;
+        optional int32 nice = 3;
+        optional int32 sys = 4;
+        optional int32 idle = 5;
+        optional int32 iow = 6;
+        optional int32 irq = 7;
+        optional int32 sirq = 8;
+        optional int32 host = 9;
+    }
+    optional CpuUsage cpu_usage = 4;
+
+    // Next Tag: 13
+    message Task {
+        option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+        optional int32 pid = 1;
+        optional int32 tid = 2;
+        optional string user = 3;
+        optional string pr = 4;     // priority of each task, using string type is because special value RT (real time)
+        optional sint32 ni = 5;     // niceness value
+        optional float cpu = 6;     // precentage of cpu usage of the task
+
+        enum Status {
+            option (stream_proto.stream_enum).enable_enums_mapping = true;
+
+            STATUS_UNKNOWN = 0;
+            STATUS_D = 1;  // uninterruptible sleep
+            STATUS_R = 2;  // running
+            STATUS_S = 3;  // sleeping
+            STATUS_T = 4;  // traced or stopped
+            STATUS_Z = 5;  // zombie
+        }
+        optional Status s = 7;      // process status
+        optional string virt = 8;   // virtual memory size, i.e. 14.0G, 13.5M
+        optional string res = 9;    // Resident size, i.e. 0, 3.1G
+
+        // How Android memory manager will treat the task
+        enum Policy {
+            option (stream_proto.stream_enum).enable_enums_mapping = true;
+
+            POLICY_UNKNOWN = 0;
+            POLICY_fg = 1;  // foreground, the name is lower case for parsing the value
+            POLICY_bg = 2;  // background, the name is lower case for parsing the value
+            POLICY_ta = 3;  // TODO: figure out what is this value
+        }
+        optional Policy pcy = 10;   // Policy of the task
+        optional string cmd = 11;   // thread name
+        optional string name = 12;  // program name
+    }
+    repeated Task tasks = 5;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index e998b09..4c3e937 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,11 +20,12 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/libs/incident/proto/android/section.proto";
-import "frameworks/base/core/proto/android/providers/settings.proto";
+import "frameworks/base/core/proto/android/os/cpuinfo.proto";
 import "frameworks/base/core/proto/android/os/incidentheader.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
 import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
 import "frameworks/base/core/proto/android/os/procrank.proto";
+import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
 import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
 import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
@@ -68,6 +69,11 @@
         (section).args = "/d/wakeup_sources"
     ];
 
+    optional CpuInfo cpu_info = 2003 [
+        (section).type = SECTION_COMMAND,
+        (section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
+    ];
+
 
     // System Services
     optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
@@ -80,7 +86,11 @@
         (section).args = "netstats --proto"
     ];
 
-    optional android.providers.settings.SettingsServiceDumpProto settings = 3002;
+    optional android.providers.settings.SettingsServiceDumpProto settings = 3002 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "settings --proto"
+    ];
+
     optional android.service.appwidget.AppWidgetServiceDumpProto appwidget = 3003;
     optional android.service.notification.NotificationServiceDumpProto notification = 3004 [
         (section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index d032a45..eaad37a 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -29,7 +29,7 @@
 
 // Next Tag: 11
 message WakeupSourceProto {
-    option (stream_proto.stream).enable_fields_mapping = true;
+    option (stream_proto.stream_msg).enable_fields_mapping = true;
 
     // Name of the event which triggers application processor
     optional string name = 1;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index 22b3d73..b86ee01 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -63,7 +63,7 @@
 
 // Next tag: 9
 message BlockProto {
-    option (stream_proto.stream).enable_fields_mapping = true;
+    option (stream_proto.stream_msg).enable_fields_mapping = true;
 
     optional int32 node = 1;
 
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index 4d62a60..9945f2e 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -33,7 +33,7 @@
 
 // Next Tag: 11
 message ProcessProto {
-    option (stream_proto.stream).enable_fields_mapping = true;
+    option (stream_proto.stream_msg).enable_fields_mapping = true;
 
     // ID of the process
     optional int32 pid = 1;
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 3411c6a..764288c 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -39,9 +39,10 @@
     optional SystemSettingsProto system_settings = 3;
 }
 
+// Note: it's a conscious decision to add each setting as a separate field. This
+// allows annotating each setting with its own privacy tag.
 message GlobalSettingsProto {
-    // Historical operations
-    repeated SettingsOperationProto historical_op = 1;
+    repeated SettingsOperationProto historical_operations = 1;
 
     optional SettingProto add_users_when_locked = 2;
     optional SettingProto enable_accessibility_global_gesture_enabled = 3;
@@ -54,6 +55,7 @@
     optional SettingProto radio_nfc = 10;
     optional SettingProto airplane_mode_radios = 11;
     optional SettingProto airplane_mode_toggleable_radios = 12;
+    optional SettingProto bluetooth_class_of_device = 293;
     optional SettingProto bluetooth_disabled_profiles = 13;
     optional SettingProto bluetooth_interoperability_list = 14;
     optional SettingProto wifi_sleep_policy = 15;
@@ -86,6 +88,7 @@
     optional SettingProto data_roaming = 42;
     optional SettingProto mdc_initial_max_retry = 43;
     optional SettingProto force_allow_on_external = 44;
+    optional SettingProto euicc_provisioned = 294;
     optional SettingProto development_force_resizable_activities = 45;
     optional SettingProto development_enable_freeform_windows_support = 46;
     optional SettingProto development_settings_enabled = 47;
@@ -99,6 +102,11 @@
     optional SettingProto hdmi_system_audio_control_enabled = 55;
     optional SettingProto hdmi_control_auto_wakeup_enabled = 56;
     optional SettingProto hdmi_control_auto_device_off_enabled = 57;
+    optional SettingProto location_background_throttle_interval_ms = 295;
+    optional SettingProto location_background_throttle_proximity_alert_interval_ms = 296;
+    optional SettingProto location_background_throttle_package_whitelist = 297;
+    optional SettingProto wifi_scan_background_throttle_interval_ms = 298;
+    optional SettingProto wifi_scan_background_throttle_package_whitelist = 299;
     optional SettingProto mhl_input_switching_enabled = 58;
     optional SettingProto mhl_power_charge_enabled = 59;
     optional SettingProto mobile_data = 60;
@@ -109,6 +117,7 @@
     optional SettingProto netstats_time_cache_max_age = 65;
     optional SettingProto netstats_global_alert_bytes = 66;
     optional SettingProto netstats_sample_enabled = 67;
+    optional SettingProto netstats_augment_enabled = 300;
     optional SettingProto netstats_dev_bucket_duration = 68;
     optional SettingProto netstats_dev_persist_bytes = 69;
     optional SettingProto netstats_dev_rotate_age = 70;
@@ -156,6 +165,7 @@
     optional SettingProto tether_supported = 113;
     optional SettingProto tether_dun_required = 114;
     optional SettingProto tether_dun_apn = 115;
+    optional SettingProto tether_offload_disabled = 301;
     optional SettingProto carrier_app_whitelist = 116;
     optional SettingProto usb_mass_storage_enabled = 117;
     optional SettingProto use_google_mail = 118;
@@ -166,6 +176,9 @@
     optional SettingProto network_switch_notification_daily_limit = 123;
     optional SettingProto network_switch_notification_rate_limit_millis = 124;
     optional SettingProto network_avoid_bad_wifi = 125;
+    optional SettingProto network_metered_multipath_preference = 302;
+    optional SettingProto network_watchlist_last_report_time = 303;
+    optional SettingProto wifi_badging_thresholds = 304;
     optional SettingProto wifi_display_on = 126;
     optional SettingProto wifi_display_certification_on = 127;
     optional SettingProto wifi_display_wps_config = 128;
@@ -179,7 +192,14 @@
     optional SettingProto wifi_on = 136;
     optional SettingProto wifi_scan_always_available = 137;
     optional SettingProto wifi_wakeup_enabled = 138;
+    optional SettingProto wifi_wakeup_available = 305;
+    optional SettingProto network_scoring_ui_enabled = 306;
+    optional SettingProto speed_label_cache_eviction_age_millis = 307;
+    optional SettingProto recommended_network_evaluator_cache_expiry_ms = 308;
     optional SettingProto network_recommendations_enabled = 139;
+    optional SettingProto network_recommendations_package = 286;
+    optional SettingProto use_open_wifi_package = 309;
+    optional SettingProto network_recommendation_request_timeout_ms = 310;
     optional SettingProto ble_scan_always_available = 140;
     optional SettingProto wifi_saved_state = 141;
     optional SettingProto wifi_supplicant_scan_interval_ms = 142;
@@ -219,15 +239,19 @@
     optional SettingProto sys_storage_threshold_percentage = 176;
     optional SettingProto sys_storage_threshold_max_bytes = 177;
     optional SettingProto sys_storage_full_threshold_bytes = 178;
+    optional SettingProto sys_storage_cache_percentage = 311;
+    optional SettingProto sys_storage_cache_max_bytes = 312;
     optional SettingProto sync_max_retry_delay_in_seconds = 179;
     optional SettingProto connectivity_change_delay = 180;
     optional SettingProto connectivity_sampling_interval_in_seconds = 181;
     optional SettingProto pac_change_delay = 182;
     optional SettingProto captive_portal_mode = 183;
+    optional SettingProto captive_portal_detection_enabled = 313;
     optional SettingProto captive_portal_server = 184;
     optional SettingProto captive_portal_https_url = 185;
     optional SettingProto captive_portal_http_url = 186;
     optional SettingProto captive_portal_fallback_url = 187;
+    optional SettingProto captive_portal_other_fallback_urls = 314;
     optional SettingProto captive_portal_use_https = 188;
     optional SettingProto captive_portal_user_agent = 189;
     optional SettingProto nsd_on = 190;
@@ -243,21 +267,33 @@
     optional SettingProto global_http_proxy_pac = 200;
     optional SettingProto set_global_http_proxy = 201;
     optional SettingProto default_dns_server = 202;
+    // The requested Private DNS mode and an accompanying specifier.
+    optional SettingProto private_dns_mode = 315;
+    optional SettingProto private_dns_specifier = 316;
     optional SettingProto bluetooth_headset_priority_prefix = 203;
     optional SettingProto bluetooth_a2dp_sink_priority_prefix = 204;
     optional SettingProto bluetooth_a2dp_src_priority_prefix = 205;
+    optional SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
+    optional SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
     optional SettingProto bluetooth_input_device_priority_prefix = 206;
     optional SettingProto bluetooth_map_priority_prefix = 207;
     optional SettingProto bluetooth_map_client_priority_prefix = 208;
     optional SettingProto bluetooth_pbap_client_priority_prefix = 209;
     optional SettingProto bluetooth_sap_priority_prefix = 210;
     optional SettingProto bluetooth_pan_priority_prefix = 211;
+    optional SettingProto activity_manager_constants = 317;
     optional SettingProto device_idle_constants = 212;
     optional SettingProto device_idle_constants_watch = 213;
+    optional SettingProto battery_saver_constants = 318;
+    optional SettingProto anomaly_detection_constants = 319;
+    optional SettingProto always_on_display_constants = 320;
     optional SettingProto app_idle_constants = 214;
+    optional SettingProto power_manager_constants = 321;
     optional SettingProto alarm_manager_constants = 215;
     optional SettingProto job_scheduler_constants = 216;
     optional SettingProto shortcut_manager_constants = 217;
+    optional SettingProto device_policy_constants = 322;
+    optional SettingProto text_classifier_constants = 323;
     optional SettingProto window_animation_scale = 218;
     optional SettingProto transition_animation_scale = 219;
     optional SettingProto animator_duration_scale = 220;
@@ -287,6 +323,10 @@
     optional SettingProto cert_pin_update_metadata_url = 244;
     optional SettingProto intent_firewall_update_content_url = 245;
     optional SettingProto intent_firewall_update_metadata_url = 246;
+    optional SettingProto lang_id_update_content_url = 324;
+    optional SettingProto lang_id_update_metadata_url = 325;
+    optional SettingProto smart_selection_update_content_url = 326;
+    optional SettingProto smart_selection_update_metadata_url = 327;
     optional SettingProto selinux_status = 247;
     optional SettingProto development_force_rtl = 248;
     optional SettingProto low_battery_sound_timeout = 249;
@@ -308,13 +348,24 @@
     optional SettingProto lte_service_forced = 265;
     optional SettingProto ephemeral_cookie_max_size_bytes = 266;
     optional SettingProto enable_ephemeral_feature = 267;
+    optional SettingProto instant_app_dexopt_enabled = 328;
     optional SettingProto installed_instant_app_min_cache_period = 268;
+    optional SettingProto installed_instant_app_max_cache_period = 289;
+    optional SettingProto uninstalled_instant_app_min_cache_period = 290;
+    optional SettingProto uninstalled_instant_app_max_cache_period = 291;
+    optional SettingProto unused_static_shared_lib_min_cache_period = 292;
     optional SettingProto allow_user_switching_when_system_user_locked = 269;
     optional SettingProto boot_count = 270;
     optional SettingProto safe_boot_disallowed = 271;
     optional SettingProto device_demo_mode = 272;
+    optional SettingProto network_access_timeout_ms = 329;
     optional SettingProto database_downgrade_reason = 274;
+    optional SettingProto database_creation_buildid = 330;
     optional SettingProto contacts_database_wal_enabled = 275;
+    optional SettingProto location_settings_link_to_permissions_enabled = 331;
+    optional SettingProto backup_refactored_service_disabled = 332;
+    optional SettingProto euicc_factory_reset_timeout_millis = 333;
+    optional SettingProto storage_settings_clobber_threshold = 334;
     optional SettingProto multi_sim_voice_call_subscription = 276;
     optional SettingProto multi_sim_voice_prompt = 277;
     optional SettingProto multi_sim_data_call_subscription = 278;
@@ -324,19 +375,20 @@
     optional SettingProto contact_metadata_sync_enabled = 282;
     optional SettingProto enable_cellular_on_boot = 283;
     optional SettingProto max_notification_enqueue_rate = 284;
+    optional SettingProto show_notification_channel_warnings = 335;
     optional SettingProto cell_on = 285;
-    optional SettingProto network_recommendations_package = 286;
-    optional SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
-    optional SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
-    optional SettingProto installed_instant_app_max_cache_period = 289;
-    optional SettingProto uninstalled_instant_app_min_cache_period = 290;
-    optional SettingProto uninstalled_instant_app_max_cache_period = 291;
-    optional SettingProto unused_static_shared_lib_min_cache_period = 292;
+    optional SettingProto show_temperature_warning = 336;
+    optional SettingProto warning_temperature = 337;
+    optional SettingProto enable_diskstats_logging = 338;
+    optional SettingProto enable_cache_quota_calculation = 339;
+    optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
+    optional SettingProto notification_snooze_options = 341;
+
+    // Next tag = 342;
 }
 
 message SecureSettingsProto {
-    // Historical operations
-    repeated SettingsOperationProto historical_op = 1;
+    repeated SettingsOperationProto historical_operations = 1;
 
     optional SettingProto android_id = 2;
     optional SettingProto default_input_method = 3;
@@ -347,6 +399,10 @@
     optional SettingProto autofill_service = 8;
     optional SettingProto bluetooth_hci_log = 9;
     optional SettingProto user_setup_complete = 10;
+    // Whether the current user has been set up via setup wizard (0 = false,
+    // 1 = true). This value differs from USER_SETUP_COMPLETE in that it can be
+    // reset back to 0 in case SetupWizard has been re-enabled on TV devices.
+    optional SettingProto tv_user_setup_complete = 170;
     optional SettingProto completed_category_prefix = 11;
     optional SettingProto enabled_input_methods = 12;
     optional SettingProto disabled_system_input_methods = 13;
@@ -354,10 +410,12 @@
     optional SettingProto always_on_vpn_app = 15;
     optional SettingProto always_on_vpn_lockdown = 16;
     optional SettingProto install_non_market_apps = 17;
+    optional SettingProto unknown_sources_default_reversed = 171;
     optional SettingProto location_mode = 18;
     optional SettingProto location_previous_mode = 19;
     optional SettingProto lock_to_app_exit_locked = 20;
     optional SettingProto lock_screen_lock_after_timeout = 21;
+    optional SettingProto lock_screen_allow_private_notifications = 172;
     optional SettingProto lock_screen_allow_remote_input = 22;
     optional SettingProto show_note_about_notification_hiding = 23;
     optional SettingProto trust_agents_initialized = 24;
@@ -366,6 +424,11 @@
     optional SettingProto parental_control_redirect_url = 27;
     optional SettingProto settings_classname = 28;
     optional SettingProto accessibility_enabled = 29;
+    optional SettingProto accessibility_shortcut_enabled = 173;
+    optional SettingProto accessibility_shortcut_on_lock_screen = 174;
+    optional SettingProto accessibility_shortcut_dialog_shown = 175;
+    optional SettingProto accessibility_shortcut_target_service = 176;
+    optional SettingProto accessibility_button_target_component = 177;
     optional SettingProto touch_exploration_enabled = 30;
     optional SettingProto enabled_accessibility_services = 31;
     optional SettingProto touch_exploration_granted_accessibility_services = 32;
@@ -375,7 +438,9 @@
     optional SettingProto accessibility_screen_reader_url = 36;
     optional SettingProto accessibility_web_content_key_bindings = 37;
     optional SettingProto accessibility_display_magnification_enabled = 38;
+    optional SettingProto accessibility_display_magnification_navbar_enabled = 178;
     optional SettingProto accessibility_display_magnification_scale = 39;
+    optional SettingProto accessibility_display_magnification_auto_update = 179;
     optional SettingProto accessibility_soft_keyboard_mode = 40;
     optional SettingProto accessibility_captioning_enabled = 41;
     optional SettingProto accessibility_captioning_locale = 42;
@@ -448,6 +513,7 @@
     optional SettingProto doze_enabled = 109;
     optional SettingProto doze_always_on = 110;
     optional SettingProto doze_pulse_on_pick_up = 111;
+    optional SettingProto doze_pulse_on_long_press = 180;
     optional SettingProto doze_pulse_on_double_tap = 112;
     optional SettingProto ui_night_mode = 113;
     optional SettingProto screensaver_enabled = 114;
@@ -470,6 +536,7 @@
     optional SettingProto immersive_mode_confirmations = 131;
     optional SettingProto print_service_search_uri = 132;
     optional SettingProto payment_service_search_uri = 133;
+    optional SettingProto autofill_service_search_uri = 181;
     optional SettingProto skip_first_use_hints = 134;
     optional SettingProto unsafe_volume_music_active_ms = 135;
     optional SettingProto lock_screen_show_notifications = 136;
@@ -482,10 +549,18 @@
     optional SettingProto camera_gesture_disabled = 143;
     optional SettingProto camera_double_tap_power_gesture_disabled = 144;
     optional SettingProto camera_double_twist_to_flip_enabled = 145;
+    optional SettingProto camera_lift_trigger_enabled = 182;
+    optional SettingProto assist_gesture_enabled = 183;
+    optional SettingProto assist_gesture_sensitivity = 184;
+    optional SettingProto assist_gesture_silence_alerts_enabled = 185;
+    optional SettingProto assist_gesture_wake_enabled = 186;
+    optional SettingProto assist_gesture_setup_complete = 187;
     optional SettingProto night_display_activated = 146;
     optional SettingProto night_display_auto_mode = 147;
+    optional SettingProto night_display_color_temperature = 188;
     optional SettingProto night_display_custom_start_time = 148;
     optional SettingProto night_display_custom_end_time = 149;
+    optional SettingProto night_display_last_activated_time = 189;
     optional SettingProto brightness_use_twilight = 150;
     optional SettingProto enabled_vr_listeners = 151;
     optional SettingProto vr_display_mode = 152;
@@ -495,6 +570,7 @@
     optional SettingProto automatic_storage_manager_days_to_retain = 156;
     optional SettingProto automatic_storage_manager_bytes_cleared = 157;
     optional SettingProto automatic_storage_manager_last_run = 158;
+    optional SettingProto automatic_storage_manager_turned_off_by_policy = 190;
     optional SettingProto system_navigation_keys_enabled = 159;
     optional SettingProto downloads_backup_enabled = 160;
     optional SettingProto downloads_backup_allow_metered = 161;
@@ -504,13 +580,18 @@
     optional SettingProto demo_user_setup_complete = 165;
     optional SettingProto instant_apps_enabled = 166;
     optional SettingProto device_paired = 167;
+    optional SettingProto package_verifier_state = 191;
+    optional SettingProto cmas_additional_broadcast_pkg = 192;
     optional SettingProto notification_badging = 168;
+    optional SettingProto qs_auto_added_tiles = 193;
+    optional SettingProto lockdown_in_power_menu = 194;
     optional SettingProto backup_manager_constants = 169;
+
+    // Next tag = 195
 }
 
 message SystemSettingsProto {
-    // Historical operations
-    repeated SettingsOperationProto historical_op = 1;
+    repeated SettingsOperationProto historical_operations = 1;
 
     optional SettingProto end_button_behavior = 2;
     optional SettingProto advanced_settings = 3;
@@ -518,6 +599,7 @@
     optional SettingProto bluetooth_discoverability_timeout = 5;
     optional SettingProto font_scale = 6;
     optional SettingProto system_locales = 7;
+    optional SettingProto display_color_mode = 67;
     optional SettingProto screen_off_timeout = 8;
     optional SettingProto screen_brightness = 9;
     optional SettingProto screen_brightness_for_vr = 10;
@@ -534,8 +616,17 @@
     optional SettingProto volume_alarm = 21;
     optional SettingProto volume_notification = 22;
     optional SettingProto volume_bluetooth_sco = 23;
+    optional SettingProto volume_accessibility = 68;
     optional SettingProto volume_master = 24;
     optional SettingProto master_mono = 25;
+    // Whether silent mode should allow vibration feedback. This is used
+    // internally in AudioService and the Sound settings activity to coordinate
+    // decoupling of vibrate and silent modes. This setting will likely be
+    // removed in a future release with support for audio/vibe feedback
+    // profiles.
+    // Not used anymore. On devices with vibrator, the user explicitly selects
+    // silent or vibrate mode. Kept for use by legacy database upgrade code in
+    // DatabaseHelper.
     optional SettingProto vibrate_in_silent = 26;
     optional SettingProto append_for_last_audible = 27;
     optional SettingProto ringtone = 28;
@@ -566,6 +657,10 @@
     optional SettingProto notification_light_pulse = 53;
     optional SettingProto pointer_location = 54;
     optional SettingProto show_touches = 55;
+    // Log raw orientation data from {@link
+    // com.android.server.policy.WindowOrientationListener} for use with the
+    // orientationplot.py tool.
+    // 0 = no, 1 = yes
     optional SettingProto window_orientation_listener_log = 56;
     optional SettingProto lockscreen_sounds_enabled = 57;
     optional SettingProto lockscreen_disabled = 58;
@@ -576,7 +671,10 @@
     optional SettingProto pointer_speed = 63;
     optional SettingProto lock_to_app_enabled = 64;
     optional SettingProto egg_mode = 65;
+    optional SettingProto show_battery_percent = 69;
     optional SettingProto when_to_make_wifi_calls = 66;
+
+    // Next tag = 70;
 }
 
 message SettingProto {
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index d442acf..d3b2cde 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -145,7 +145,7 @@
     optional bool is_holding_display_suspend_blocker = 39;
     // Settings and configuration
     optional PowerServiceSettingsAndConfigurationDumpProto settings_and_configuration = 40;
-    // Sleep timeout in ms
+    // Sleep timeout in ms. This can be -1.
     optional sint32 sleep_timeout_ms = 41;
     // Screen off timeout in ms
     optional int32 screen_off_timeout_ms = 42;
@@ -263,7 +263,7 @@
     optional float maximum_screen_dim_ratio_config = 24;
     // The screen off timeout setting value in milliseconds.
     optional int32 screen_off_timeout_setting_ms = 25;
-    // The sleep timeout setting value in milliseconds.
+    // The sleep timeout setting value in milliseconds. Default value is -1.
     optional sint32 sleep_timeout_setting_ms = 26;
     // The maximum allowable screen off timeout according to the device administration policy.
     optional int32 maximum_screen_off_timeout_from_device_admin_ms = 27;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 4d48a42..0228edb 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -48,7 +48,6 @@
 
 /* represents PhoneWindowManager */
 message WindowManagerPolicyProto {
-  optional .android.graphics.RectProto stable_bounds = 1;
 }
 
 /* represents AppTransition */
@@ -101,8 +100,13 @@
   optional .android.view.DisplayInfoProto display_info = 10;
   optional int32 rotation = 11;
   optional ScreenRotationAnimationProto screen_rotation_animation = 12;
+  optional DisplayFramesProto display_frames = 13;
 }
 
+/* represents DisplayFrames */
+message DisplayFramesProto {
+  optional .android.graphics.RectProto stable_bounds = 1;
+}
 
 /* represents DockedStackDividerController */
 message DockedStackDividerControllerProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 49de135..7af1b46 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3657,7 +3657,7 @@
         </activity-alias>
         <activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
                 android:targetActivity="com.android.internal.app.IntentForwarderActivity"
-                android:icon="@drawable/ic_corp_icon"
+                android:icon="@drawable/ic_corp_badge"
                 android:exported="true"
                 android:label="@string/managed_profile_label">
         </activity-alias>
@@ -3944,6 +3944,10 @@
         <service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
+
+        <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
     </application>
 
 </manifest>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1c2e5a4..1879ab7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -123,6 +123,9 @@
          be sent during a change to the audio output device. -->
     <bool name="config_sendAudioBecomingNoisy">true</bool>
 
+    <!-- Flag to disable all transition animations -->
+    <bool name="config_disableTransitionAnimation">false</bool>
+
     <!-- The duration (in milliseconds) of a short animation. -->
     <integer name="config_shortAnimTime">200</integer>
 
@@ -876,7 +879,7 @@
     <bool name="config_nightDisplayAvailable">@bool/config_setColorTransformAccelerated</bool>
 
     <!-- Default mode to control how Night display is automatically activated.
-         One of the following values (see NightDisplayController.java):
+         One of the following values (see ColorDisplayController.java):
              0 - AUTO_MODE_DISABLED
              1 - AUTO_MODE_CUSTOM
              2 - AUTO_MODE_TWILIGHT
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 78a8e2a..9e0722b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4744,4 +4744,7 @@
     A toast message shown when an app shortcut that wasn't restored due to an unknown issue is clicked,
     -->
     <string name="shortcut_restore_unknown_issue">Couldn\u2019t restore shortcut</string>
+
+    <!--Battery saver warning. STOPSHIP: Remove it eventually. -->
+    <string name="battery_saver_warning" translatable="false">Battery saver activated.\n\nSee go/extreme-battery-saver.\n\nThis contains aggressive experimental changes for P and may affect background app behavior.\n</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fd0012d..e476d89 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -252,6 +252,7 @@
   <java-symbol type="bool" name="config_bluetooth_hfp_inband_ringing_support" />
   <java-symbol type="bool" name="config_cellBroadcastAppLinks" />
   <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
+  <java-symbol type="bool" name="config_disableTransitionAnimation" />
   <java-symbol type="bool" name="config_enableAutoPowerModes" />
   <java-symbol type="integer" name="config_autoPowerModeThresholdAngle" />
   <java-symbol type="integer" name="config_autoPowerModeAnyMotionSensor" />
@@ -1324,6 +1325,7 @@
   <java-symbol type="drawable" name="cling_button" />
   <java-symbol type="drawable" name="cling_arrow_up" />
   <java-symbol type="drawable" name="cling_bg" />
+  <java-symbol type="drawable" name="ic_corp_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_color" />
   <java-symbol type="drawable" name="ic_corp_badge_case" />
   <java-symbol type="drawable" name="ic_corp_icon" />
@@ -3144,6 +3146,7 @@
   <java-symbol type="string" name="shortcut_restore_not_supported" />
   <java-symbol type="string" name="shortcut_restore_signature_mismatch" />
   <java-symbol type="string" name="shortcut_restore_unknown_issue" />
+  <java-symbol type="string" name="battery_saver_warning" />
 
   <!-- From media projection -->
   <java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
diff --git a/core/tests/coretests/res/layout/add_column_in_table.xml b/core/tests/coretests/res/layout/add_column_in_table.xml
index 05f55a8..d929b02 100644
--- a/core/tests/coretests/res/layout/add_column_in_table.xml
+++ b/core/tests/coretests/res/layout/add_column_in_table.xml
@@ -18,6 +18,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
+    <requestFocus />
 
     <TableLayout android:id="@+id/table"
         android:layout_width="match_parent"
diff --git a/core/tests/coretests/res/layout/baseline_0width_and_weight.xml b/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
index acbb10b..eac9b9d 100644
--- a/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
+++ b/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
@@ -21,6 +21,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
+    <requestFocus />
     <LinearLayout android:id="@+id/layout"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/core/tests/coretests/res/layout/focus_after_removal.xml b/core/tests/coretests/res/layout/focus_after_removal.xml
index f4e388d..84449d1 100644
--- a/core/tests/coretests/res/layout/focus_after_removal.xml
+++ b/core/tests/coretests/res/layout/focus_after_removal.xml
@@ -22,6 +22,7 @@
     android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
+    <requestFocus />
 
     <LinearLayout android:id="@+id/leftLayout"
         android:orientation="vertical"
diff --git a/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml b/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
index ab76e29..6b3b5a7 100644
--- a/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
+++ b/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
@@ -22,6 +22,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
+    <requestFocus />
 
     <EditText
         android:id="@+id/editText"
diff --git a/core/tests/coretests/res/layout/visibility_callback.xml b/core/tests/coretests/res/layout/visibility_callback.xml
index 9034b3f..ff918f5 100644
--- a/core/tests/coretests/res/layout/visibility_callback.xml
+++ b/core/tests/coretests/res/layout/visibility_callback.xml
@@ -24,6 +24,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
+    <requestFocus />
 
     <LinearLayout
       android:orientation="vertical"
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 1002939..d36ed63 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -26,7 +26,6 @@
 import static java.lang.reflect.Modifier.isPublic;
 import static java.lang.reflect.Modifier.isStatic;
 
-import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -264,6 +263,7 @@
                     Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE,
                     Settings.Global.NETWORK_AVOID_BAD_WIFI,
                     Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE,
+                    Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
                     Settings.Global.NETWORK_PREFERENCE,
                     Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE,
                     Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS,
diff --git a/core/tests/coretests/src/android/util/ListScenario.java b/core/tests/coretests/src/android/util/ListScenario.java
index fa088a3..129484a 100644
--- a/core/tests/coretests/src/android/util/ListScenario.java
+++ b/core/tests/coretests/src/android/util/ListScenario.java
@@ -28,6 +28,7 @@
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
+
 import com.google.android.collect.Maps;
 
 import java.util.ArrayList;
@@ -60,18 +61,18 @@
 
     // separators
     private Set<Integer> mUnselectableItems = new HashSet<Integer>();
-    
+
     private boolean mStackFromBottom;
 
     private int mClickedPosition = -1;
-    
+
     private int mLongClickedPosition = -1;
-    
+
     private int mConvertMisses = 0;
-    
+
     private int mHeaderViewCount;
     private boolean mHeadersFocusable;
-    
+
     private int mFooterViewCount;
     private LinearLayout mLinearLayout;
 
@@ -193,7 +194,7 @@
             mIncludeHeader = includeHeader;
             return this;
         }
-        
+
         /**
          * Sets the stacking direction
          * @param stackFromBottom
@@ -203,7 +204,7 @@
             mStackFromBottom = stackFromBottom;
             return this;
         }
-        
+
         /**
          * Sets whether the sum of the height of the list items must be at least the
          * height of the list view.
@@ -220,7 +221,7 @@
             mFadingEdgeScreenSizeFactor = fadingEdgeScreenSizeFactor;
             return this;
         }
-        
+
         /**
          * Set the number of header views to appear within the list
          */
@@ -246,7 +247,7 @@
             mFooterViewCount = footerViewCount;
             return this;
         }
-        
+
         /**
          * Sets whether the {@link ListScenario} will automatically set the
          * adapter on the list view. If this is false, the client MUST set it
@@ -278,7 +279,7 @@
      */
     protected void nothingSelected() {
     }
-    
+
     /**
      * Override this if you want to know when something has been clicked (perhaps
      * more importantly, that {@link android.widget.AdapterView.OnItemClickListener} has
@@ -287,7 +288,7 @@
     protected void positionClicked(int position) {
         setClickedPosition(position);
     }
-    
+
     /**
      * Override this if you want to know when something has been long clicked (perhaps
      * more importantly, that {@link android.widget.AdapterView.OnItemLongClickListener} has
@@ -303,7 +304,7 @@
 
         // for test stability, turn off title bar
         requestWindowFeature(Window.FEATURE_NO_TITLE);
-        
+
 
         mScreenHeight = getWindowManager().getDefaultDisplay().getHeight();
 
@@ -326,7 +327,7 @@
             header.setText("Header: " + i);
             mListView.addHeaderView(header);
         }
-        
+
         for (int i=0; i<mFooterViewCount; i++) {
             TextView header = new TextView(this);
             header.setText("Footer: " + i);
@@ -336,7 +337,7 @@
         if (params.mConnectAdapter) {
             setAdapter(mListView);
         }
-        
+
         mListView.setItemsCanFocus(mItemsFocusable);
         if (mStartingSelectionPosition >= 0) {
             mListView.setSelection(mStartingSelectionPosition);
@@ -360,11 +361,12 @@
                 positionClicked(position);
             }
         });
-        
+
         // set the fading edge length porportionally to the screen
         // height for test stability
         if (params.mFadingEdgeScreenSizeFactor != null) {
-            mListView.setFadingEdgeLength((int) (params.mFadingEdgeScreenSizeFactor * mScreenHeight));            
+            mListView.setFadingEdgeLength(
+                    (int) (params.mFadingEdgeScreenSizeFactor * mScreenHeight));
         } else {
             mListView.setFadingEdgeLength((int) ((64.0 / 480) * mScreenHeight));
         }
@@ -403,6 +405,7 @@
             mLinearLayout.addView(mListView);
             setContentView(mLinearLayout);
         }
+        mLinearLayout.restoreDefaultFocus();
     }
 
     /**
@@ -426,7 +429,7 @@
             }
         });
     }
-    
+
     /**
      * @return The newly created ListView widget.
      */
@@ -440,16 +443,16 @@
     protected Params createParams() {
         return new Params();
     }
-    
+
     /**
      * Sets an adapter on a ListView.
-     * 
+     *
      * @param listView The ListView to set the adapter on.
      */
     protected void setAdapter(ListView listView) {
         listView.setAdapter(new MyAdapter());
     }
-    
+
     /**
      * Read in and validate all of the params passed in by the scenario.
      * @param params
@@ -525,7 +528,7 @@
         if (!mIncludeHeader) {
             throw new IllegalArgumentException("no header above list");
         }
-        mHeaderTextView.setText(value);        
+        mHeaderTextView.setText(value);
     }
 
     /**
@@ -543,12 +546,12 @@
     }
 
     /**
-     * Convert a non-null view. 
+     * Convert a non-null view.
      */
     public View convertView(int position, View convertView, ViewGroup parent) {
         return ListItemFactory.convertText(convertView, getValueAtPosition(position), position);
     }
-    
+
     public void setClickedPosition(int clickedPosition) {
         mClickedPosition = clickedPosition;
     }
@@ -580,7 +583,7 @@
             }
         });
     }
-    
+
     /**
      * Return an item type for the specified position in the adapter. Override if your
      * adapter creates more than one type.
@@ -596,7 +599,7 @@
     public int getViewTypeCount() {
         return 1;
     }
-    
+
     /**
      * @return The number of times convertView failed
      */
@@ -647,7 +650,7 @@
             }
             return result;
         }
-        
+
         @Override
         public int getItemViewType(int position) {
             return ListScenario.this.getItemViewType(position);
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index b6986d5..0355f82 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -48,6 +48,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Predicate;
 
 /**
  * Espresso utility methods for the floating toolbar.
@@ -175,31 +176,10 @@
      * @throws AssertionError if the assertion fails
      */
     public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
-        onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
-            @Override
-            public boolean matchesSafely(View view) {
-                return doesNotContainItem(view);
-            }
-
-            @Override
-            public void describeTo(Description description) {}
-
-            private boolean doesNotContainItem(View view) {
-                if (view.getTag() instanceof MenuItem) {
-                    if (itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString())) {
-                        return false;
-                    }
-                } else if (view instanceof ViewGroup) {
-                    ViewGroup viewGroup = (ViewGroup) view;
-                    for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                        if (doesNotContainItem(viewGroup.getChildAt(i))) {
-                            return false;
-                        }
-                    }
-                }
-                return true;
-            }
-        }));
+        final Predicate<View> hasMenuItemLabel = view ->
+                view.getTag() instanceof MenuItem
+                        && itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString());
+        assertFloatingToolbarMenuItem(hasMenuItemLabel, false);
     }
 
     /**
@@ -209,23 +189,30 @@
      * @throws AssertionError if the assertion fails
      */
     public static void assertFloatingToolbarDoesNotContainItem(final int menuItemId) {
+        final Predicate<View> hasMenuItemId = view ->
+                view.getTag() instanceof MenuItem
+                        && ((MenuItem) view.getTag()).getItemId() == menuItemId;
+        assertFloatingToolbarMenuItem(hasMenuItemId, false);
+    }
+
+    private static void assertFloatingToolbarMenuItem(
+            final Predicate<View> predicate, final boolean positiveAssertion) {
         onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
             @Override
             public boolean matchesSafely(View view) {
-                return !hasMenuItemWithSpecifiedId(view);
+                return positiveAssertion == containsItem(view);
             }
 
             @Override
             public void describeTo(Description description) {}
 
-            private boolean hasMenuItemWithSpecifiedId(View view) {
-                if (view.getTag() instanceof MenuItem
-                        && ((MenuItem) view.getTag()).getItemId() == menuItemId) {
+            private boolean containsItem(View view) {
+                if (predicate.test(view)) {
                     return true;
                 } else if (view instanceof ViewGroup) {
                     ViewGroup viewGroup = (ViewGroup) view;
                     for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                        if (hasMenuItemWithSpecifiedId(viewGroup.getChildAt(i))) {
+                        if (containsItem(viewGroup.getChildAt(i))) {
                             return true;
                         }
                     }
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
index 1ba56ba..0d8d834 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
@@ -16,12 +16,12 @@
 
 package android.widget.layout.linear;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
 
+import com.android.frameworks.coretests.R;
+
 public class LLOfTwoFocusableInTouchMode extends Activity {
 
     private View mButton1;
@@ -63,6 +63,7 @@
                 mB3Fired = true;
             }
         });
+        getWindow().getDecorView().restoreDefaultFocus();
     }
 
     public View getButton1() {
diff --git a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
index 98fbed3..9e49719 100644
--- a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
+++ b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
@@ -16,10 +16,9 @@
 
 package android.widget.listview;
 
-import android.util.InternalSelectionView;
-
 import android.app.Activity;
 import android.os.Bundle;
+import android.util.InternalSelectionView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
@@ -80,6 +79,7 @@
 
 
         setContentView(combineAdjacent(mLeftListView, mRightListView));
+        getWindow().getDecorView().restoreDefaultFocus();
     }
 
     private static View combineAdjacent(View... views) {
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
index 8f971bb..edc60b5 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
@@ -16,15 +16,12 @@
 
 package android.widget.listview.focus;
 
-import android.widget.listview.ListHorizontalFocusWithinItemWins;
-
 import android.test.ActivityInstrumentationTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.view.FocusFinder;
 import android.view.KeyEvent;
-import android.view.View;
 import android.widget.Button;
 import android.widget.ListView;
+import android.widget.listview.ListHorizontalFocusWithinItemWins;
 
 public class ListHorizontalFocusWithinItemWinsTest extends ActivityInstrumentationTestCase<ListHorizontalFocusWithinItemWins> {
 
@@ -51,12 +48,6 @@
     public void testPreconditions() {
         assertEquals("list position", 0, mListView.getSelectedItemPosition());
         assertTrue("mTopLeftButton.isFocused()", mTopLeftButton.isFocused());
-        assertEquals("global focus search to right from top left is bottom middle",
-                mBottomMiddleButton,
-                FocusFinder.getInstance().findNextFocus(mListView, mTopLeftButton, View.FOCUS_RIGHT));
-        assertEquals("global focus search to left from top right is bottom middle",
-                mBottomMiddleButton,
-                FocusFinder.getInstance().findNextFocus(mListView, mTopRightButton, View.FOCUS_LEFT));
     }
 
     @MediumTest
diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
index bd6977e..5a6110c 100644
--- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
@@ -16,7 +16,6 @@
 
 package android.widget.touchmode;
 
-import android.widget.layout.linear.LLOfButtons1;
 import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterClick;
 import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap;
 import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
@@ -25,6 +24,8 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.view.KeyEvent;
 import android.widget.Button;
+import android.widget.layout.linear.LLOfButtons1;
+
 
 /**
  * Make sure focus isn't kept by buttons when entering touch mode.
@@ -52,7 +53,6 @@
     @MediumTest
     public void testPreconditions() {
         assertFalse("we should not be in touch mode", mActivity.isInTouchMode());
-        assertTrue("top button should have focus", mFirstButton.isFocused());
     }
 
     @MediumTest
diff --git a/core/tests/webkit/apk_with_native_libs/Android.mk b/core/tests/webkit/apk_with_native_libs/Android.mk
index 7c6c36e..c51de6a 100644
--- a/core/tests/webkit/apk_with_native_libs/Android.mk
+++ b/core/tests/webkit/apk_with_native_libs/Android.mk
@@ -20,6 +20,7 @@
 MY_JNI_SHARED_LIBRARIES := libwebviewtest_jni
 MY_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 MY_SRC_FILES := $(call all-java-files-under, src)
+MY_CFLAGS := -Wall -Werror
 MY_SDK_VERSION := system_current
 MY_PROGUARD_ENABLED := disabled
 MY_MULTILIB := both
@@ -40,6 +41,7 @@
 LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
 LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
 LOCAL_SRC_FILES := $(MY_SRC_FILES)
+LOCAL_CFLAGS := $(MY_CFLAGS)
 LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
 LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
 LOCAL_MULTILIB := $(MY_MULTILIB)
@@ -59,6 +61,7 @@
 LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
 LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
 LOCAL_SRC_FILES := $(MY_SRC_FILES)
+LOCAL_CFLAGS := $(MY_CFLAGS)
 LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
 LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
 LOCAL_MULTILIB := $(MY_MULTILIB)
diff --git a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
index 9b0502f..0ced4ee 100644
--- a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
+++ b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
@@ -16,6 +16,6 @@
 
 #include <jni.h>
 
-jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+jint JNI_OnLoad(JavaVM * /*vm*/, void * /*reserved*/) {
     return JNI_VERSION_1_4;
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f7e0b46..161a0c2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -258,6 +258,11 @@
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.settings.intelligence">
+        <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.sharedstoragebackup">
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 3dc928d..aff942d 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -475,6 +475,19 @@
     }
 
     /**
+     * If the specified rectangle intersects this rectangle, set this rectangle to that
+     * intersection, otherwise set this rectangle to the empty rectangle.
+     * @see #inset(int, int, int, int) but without checking if the rects overlap.
+     * @hide
+     */
+    public void intersectUnchecked(Rect other) {
+        left = Math.max(left, other.left);
+        top = Math.max(top, other.top);
+        right = Math.min(right, other.right);
+        bottom = Math.min(bottom, other.bottom);
+    }
+
+    /**
      * If rectangles a and b intersect, return true and set this rectangle to
      * that intersection, otherwise return false and do not change this
      * rectangle. No check is performed to see if either rectangle is empty.
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index c4bb72c..635432d 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -28,6 +28,8 @@
     String requestPrivateKey(String alias);
     byte[] getCertificate(String alias);
     byte[] getCaCertificates(String alias);
+    boolean isUserSelectable(String alias);
+    void setUserSelectable(String alias, boolean isUserSelectable);
 
     // APIs used by CertInstaller and DevicePolicyManager
     String installCaCertificate(in byte[] caCertificate);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index a7469cd..399dddd 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -54,7 +54,7 @@
 public class KeyStore {
     private static final String TAG = "KeyStore";
 
-    // ResponseCodes
+    // ResponseCodes - see system/security/keystore/include/keystore/keystore.h
     public static final int NO_ERROR = 1;
     public static final int LOCKED = 2;
     public static final int UNINITIALIZED = 3;
@@ -168,10 +168,14 @@
 
     public byte[] get(String key, int uid) {
         try {
+            key = key != null ? key : "";
             return mBinder.get(key, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
+        } catch (android.os.ServiceSpecificException e) {
+            Log.w(TAG, "KeyStore exception", e);
+            return null;
         }
     }
 
@@ -185,6 +189,9 @@
 
     public int insert(String key, byte[] value, int uid, int flags) {
         try {
+            if (value == null) {
+                value = new byte[0];
+            }
             return mBinder.insert(key, value, uid, flags);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -228,6 +235,9 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
+        } catch (android.os.ServiceSpecificException e) {
+            Log.w(TAG, "KeyStore exception", e);
+            return null;
         }
     }
 
@@ -276,6 +286,7 @@
      */
     public boolean unlock(int userId, String password) {
         try {
+            password = password != null ? password : "";
             mError = mBinder.unlock(userId, password);
             return mError == NO_ERROR;
         } catch (RemoteException e) {
@@ -330,16 +341,25 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
+        } catch (android.os.ServiceSpecificException e) {
+            Log.w(TAG, "KeyStore exception", e);
+            return null;
         }
+
     }
 
     public boolean verify(String key, byte[] data, byte[] signature) {
         try {
+            signature = signature != null ? signature : new byte[0];
             return mBinder.verify(key, data, signature) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
+        } catch (android.os.ServiceSpecificException e) {
+            Log.w(TAG, "KeyStore exception", e);
+            return false;
         }
+
     }
 
     public String grant(String key, int uid) {
@@ -432,6 +452,8 @@
     public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
             int flags, KeyCharacteristics outCharacteristics) {
         try {
+            entropy = entropy != null ? entropy : new byte[0];
+            args = args != null ? args : new KeymasterArguments();
             return mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -447,6 +469,8 @@
     public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
             int uid, KeyCharacteristics outCharacteristics) {
         try {
+            clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
+            appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
             return mBinder.getKeyCharacteristics(alias, clientId, appId, uid, outCharacteristics);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -478,6 +502,8 @@
     public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
             KeymasterBlob appId, int uid) {
         try {
+            clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
+            appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
             return mBinder.exportKey(alias, format, clientId, appId, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -492,6 +518,8 @@
     public OperationResult begin(String alias, int purpose, boolean pruneable,
             KeymasterArguments args, byte[] entropy, int uid) {
         try {
+            args = args != null ? args : new KeymasterArguments();
+            entropy = entropy != null ? entropy : new byte[0];
             return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -501,11 +529,15 @@
 
     public OperationResult begin(String alias, int purpose, boolean pruneable,
             KeymasterArguments args, byte[] entropy) {
+        entropy = entropy != null ? entropy : new byte[0];
+        args = args != null ? args : new KeymasterArguments();
         return begin(alias, purpose, pruneable, args, entropy, UID_SELF);
     }
 
     public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
         try {
+            arguments = arguments != null ? arguments : new KeymasterArguments();
+            input = input != null ? input : new byte[0];
             return mBinder.update(token, arguments, input);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -516,6 +548,9 @@
     public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature,
             byte[] entropy) {
         try {
+            arguments = arguments != null ? arguments : new KeymasterArguments();
+            entropy = entropy != null ? entropy : new byte[0];
+            signature = signature != null ? signature : new byte[0];
             return mBinder.finish(token, arguments, signature, entropy);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -632,6 +667,12 @@
     public int attestKey(
             String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
         try {
+            if (params == null) {
+                params = new KeymasterArguments();
+            }
+            if (outChain == null) {
+                outChain = new KeymasterCertificateChain();
+            }
             return mBinder.attestKey(alias, params, outChain);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
@@ -641,6 +682,12 @@
 
     public int attestDeviceIds(KeymasterArguments params, KeymasterCertificateChain outChain) {
         try {
+            if (params == null) {
+                params = new KeymasterArguments();
+            }
+            if (outChain == null) {
+                outChain = new KeymasterCertificateChain();
+            }
             return mBinder.attestDeviceIds(params, outChain);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 5484cf0..251b2e7 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -98,6 +98,9 @@
             enabled: true,
         },
     },
+    sanitize: {
+        blacklist: "libandroidfw_blacklist.txt",
+    },
 }
 
 common_test_libs = [
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5d248c2..3fe75cf 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -812,7 +812,13 @@
             *outLen = encLen;
 
             if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
-                return (const char*)str;
+                // Reject malformed (non null-terminated) strings
+                if (str[encLen] != 0x00) {
+                    ALOGW("Bad string block: string #%d is not null-terminated",
+                          (int)idx);
+                    return NULL;
+                }
+              return (const char*)str;
             } else {
                 ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
                         (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
diff --git a/libs/androidfw/libandroidfw_blacklist.txt b/libs/androidfw/libandroidfw_blacklist.txt
new file mode 100644
index 0000000..dd17e4d
--- /dev/null
+++ b/libs/androidfw/libandroidfw_blacklist.txt
@@ -0,0 +1 @@
+src:*/ResourceTypes.cpp
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 2644292..24ce1e4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -68,6 +68,7 @@
     ],
     static_libs: [
         "libplatformprotos",
+        "libEGL_blobCache",
     ],
 }
 
@@ -131,6 +132,7 @@
         "pipeline/skia/LayerDrawable.cpp",
         "pipeline/skia/RenderNodeDrawable.cpp",
         "pipeline/skia/ReorderBarrierDrawables.cpp",
+        "pipeline/skia/ShaderCache.cpp",
         "pipeline/skia/SkiaDisplayList.cpp",
         "pipeline/skia/SkiaOpenGLPipeline.cpp",
         "pipeline/skia/SkiaOpenGLReadback.cpp",
@@ -346,6 +348,7 @@
         "tests/unit/RecordingCanvasTests.cpp",
         "tests/unit/RenderNodeTests.cpp",
         "tests/unit/RenderPropertiesTests.cpp",
+        "tests/unit/ShaderCacheTests.cpp",
         "tests/unit/SkiaBehaviorTests.cpp",
         "tests/unit/SkiaDisplayListTests.cpp",
         "tests/unit/SkiaPipelineTests.cpp",
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index b7f1fb2..530e82e 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -31,11 +31,7 @@
 namespace uirenderer {
 
 Extensions::Extensions() {
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        // Extensions class is used only by OpenGL and SkiaGL pipelines
-        // The code below will crash for SkiaVulkan, because OpenGL is not initialized
-        // TODO: instantiate Extensions class only for OpenGL pipeline
-        // TODO: remove the only usage of Extensions by SkiaGL in SkiaOpenGLReadback::copyImageInto
+    if (Properties::isSkiaEnabled()) {
         return;
     }
     const char* version = (const char*)glGetString(GL_VERSION);
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 91756e7..5aea04d 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -245,22 +245,19 @@
     // last call to dumpData(). In other words if there's a dumpData(), draw frame,
     // dumpData(), the last dumpData() should only log 1 frame.
 
-    FILE* file = fdopen(fd, "a");
-    fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
+    dprintf(fd, "\n\tDraw\tPrepare\tProcess\tExecute\n");
 
     for (size_t i = 0; i < mFrameSource.size(); i++) {
         if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
             continue;
         }
         mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
-        fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
+        dprintf(fd, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
                 durationMS(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
                 durationMS(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
                 durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
                 durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
     }
-
-    fflush(file);
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 5b67693..afdd339 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -174,24 +174,22 @@
 }
 
 void JankTracker::dumpFrames(int fd) {
-    FILE* file = fdopen(fd, "a");
-    fprintf(file, "\n\n---PROFILEDATA---\n");
+    dprintf(fd, "\n\n---PROFILEDATA---\n");
     for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
-        fprintf(file, "%s", FrameInfoNames[i].c_str());
-        fprintf(file, ",");
+        dprintf(fd, "%s", FrameInfoNames[i].c_str());
+        dprintf(fd, ",");
     }
     for (size_t i = 0; i < mFrames.size(); i++) {
         FrameInfo& frame = mFrames[i];
         if (frame[FrameInfoIndex::SyncStart] == 0) {
             continue;
         }
-        fprintf(file, "\n");
+        dprintf(fd, "\n");
         for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
-            fprintf(file, "%" PRId64 ",", frame[i]);
+            dprintf(fd, "%" PRId64 ",", frame[i]);
         }
     }
-    fprintf(file, "\n---PROFILEDATA---\n\n");
-    fflush(file);
+    dprintf(fd, "\n---PROFILEDATA---\n\n");
 }
 
 void JankTracker::reset() {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d90caf9..d41db63 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -178,7 +178,7 @@
 }
 
 RenderPipelineType Properties::getRenderPipelineType() {
-    if (RenderPipelineType::NotInitialized != sRenderPipelineType) {
+    if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
         return sRenderPipelineType;
     }
     char prop[PROPERTY_VALUE_MAX];
@@ -196,11 +196,17 @@
     return sRenderPipelineType;
 }
 
-#ifdef HWUI_GLES_WRAP_ENABLED
 void Properties::overrideRenderPipelineType(RenderPipelineType type) {
+#if !defined(HWUI_GLES_WRAP_ENABLED)
+    // If we're doing actual rendering then we can't change the renderer after it's been set.
+    // Unit tests can freely change this as often as it wants, though, as there's no actual
+    // GL rendering happening
+    if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
+        return;
+    }
+#endif
     sRenderPipelineType = type;
 }
-#endif
 
 bool Properties::isSkiaEnabled() {
     auto renderType = getRenderPipelineType();
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index fbd9b7a..9c30e4a 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -255,10 +255,8 @@
 
     static bool skpCaptureEnabled;
 
-// Used for testing only to change the render pipeline.
-#ifdef HWUI_GLES_WRAP_ENABLED
+    // Used for testing only to change the render pipeline.
     static void overrideRenderPipelineType(RenderPipelineType);
-#endif
 
 private:
     static ProfileType sProfileType;
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
new file mode 100644
index 0000000..87edd69
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ShaderCache.h"
+#include <algorithm>
+#include <log/log.h>
+#include <thread>
+#include "FileBlobCache.h"
+#include "utils/TraceUtils.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// Cache size limits.
+static const size_t maxKeySize = 1024;
+static const size_t maxValueSize = 64 * 1024;
+static const size_t maxTotalSize = 512 * 1024;
+
+ShaderCache::ShaderCache() {
+    // There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header.
+}
+
+ShaderCache ShaderCache::sCache;
+
+ShaderCache& ShaderCache::get() {
+    return sCache;
+}
+
+void ShaderCache::initShaderDiskCache() {
+    ATRACE_NAME("initShaderDiskCache");
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mFilename.length() > 0) {
+        mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
+        mInitialized = true;
+    }
+}
+
+void ShaderCache::setFilename(const char* filename) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mFilename = filename;
+}
+
+BlobCache* ShaderCache::getBlobCacheLocked() {
+    LOG_ALWAYS_FATAL_IF(!mInitialized, "ShaderCache has not been initialized");
+    return mBlobCache.get();
+}
+
+sk_sp<SkData> ShaderCache::load(const SkData& key) {
+    ATRACE_NAME("ShaderCache::load");
+    size_t keySize = key.size();
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mInitialized) {
+        ALOGE("ShaderCache::load not initialized");
+        return nullptr;
+    }
+
+    // mObservedBlobValueSize is reasonably big to avoid memory reallocation
+    // Allocate a buffer with malloc. SkData takes ownership of that allocation and will call free.
+    void* valueBuffer = malloc(mObservedBlobValueSize);
+    if (!valueBuffer) {
+        return nullptr;
+    }
+    BlobCache* bc = getBlobCacheLocked();
+    size_t valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
+    int maxTries = 3;
+    while (valueSize > mObservedBlobValueSize && maxTries > 0) {
+        mObservedBlobValueSize = std::min(valueSize, maxValueSize);
+        valueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+        if (!valueBuffer) {
+            return nullptr;
+        }
+        valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
+        maxTries--;
+    }
+    if (!valueSize) {
+        free(valueBuffer);
+        return nullptr;
+    }
+    if (valueSize > mObservedBlobValueSize) {
+        ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
+        free(valueBuffer);
+        return nullptr;
+    }
+    return SkData::MakeFromMalloc(valueBuffer, valueSize);
+}
+
+void ShaderCache::store(const SkData& key, const SkData& data) {
+    ATRACE_NAME("ShaderCache::store");
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    if (!mInitialized) {
+        ALOGE("ShaderCache::store not initialized");
+        return;
+    }
+
+    size_t valueSize = data.size();
+    size_t keySize = key.size();
+    if (keySize == 0 || valueSize == 0 || valueSize >= maxValueSize) {
+        ALOGW("ShaderCache::store: sizes %d %d not allowed", (int)keySize, (int)valueSize);
+        return;
+    }
+
+    const void* value = data.data();
+
+    BlobCache* bc = getBlobCacheLocked();
+    bc->set(key.data(), keySize, value, valueSize);
+
+    if (!mSavePending && mDeferredSaveDelay > 0) {
+        mSavePending = true;
+        std::thread deferredSaveThread([this]() {
+            sleep(mDeferredSaveDelay);
+            std::lock_guard<std::mutex> lock(mMutex);
+            ATRACE_NAME("ShaderCache::saveToDisk");
+            if (mInitialized && mBlobCache) {
+                mBlobCache->writeToFile();
+            }
+            mSavePending = false;
+        });
+        deferredSaveThread.detach();
+    }
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
new file mode 100644
index 0000000..27473d6
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cutils/compiler.h>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+#include <GrContextOptions.h>
+
+namespace android {
+
+class BlobCache;
+class FileBlobCache;
+
+namespace uirenderer {
+namespace skiapipeline {
+
+class ShaderCache : public GrContextOptions::PersistentCache {
+public:
+    /**
+     * "get" returns a pointer to the singleton ShaderCache object.  This
+     * singleton object will never be destroyed.
+     */
+    ANDROID_API static ShaderCache& get();
+
+    /**
+     * "initShaderDiskCache" loads the serialized cache contents from disk and puts the ShaderCache
+     * into an initialized state, such that it is able to insert and retrieve entries from the
+     * cache.  This should be called when HWUI pipeline is initialized.  When not in the initialized
+     * state the load and store methods will return without performing any cache operations.
+     */
+    virtual void initShaderDiskCache();
+
+    /**
+     * "setFilename" sets the name of the file that should be used to store
+     * cache contents from one program invocation to another. This function does not perform any
+     * disk operation and it should be invoked before "initShaderCache".
+     */
+    virtual void setFilename(const char* filename);
+
+    /**
+     * "load" attempts to retrieve the value blob associated with a given key
+     * blob from cache.  This will be called by Skia, when it needs to compile a new SKSL shader.
+     */
+    sk_sp<SkData> load(const SkData& key) override;
+
+    /**
+     * "store" attempts to insert a new key/value blob pair into the cache.
+     * This will be called by Skia after it compiled a new SKSL shader
+     */
+    void store(const SkData& key, const SkData& data) override;
+
+private:
+    // Creation and (the lack of) destruction is handled internally.
+    ShaderCache();
+
+    // Copying is disallowed.
+    ShaderCache(const ShaderCache&) = delete;
+    void operator=(const ShaderCache&) = delete;
+
+    /**
+     * "getBlobCacheLocked" returns the BlobCache object being used to store the
+     * key/value blob pairs.  If the BlobCache object has not yet been created,
+     * this will do so, loading the serialized cache contents from disk if
+     * possible.
+     */
+    BlobCache* getBlobCacheLocked();
+
+    /**
+     * "mInitialized" indicates whether the ShaderCache is in the initialized
+     * state.  It is initialized to false at construction time, and gets set to
+     * true when initialize is called.
+     * When in this state, the cache behaves as normal.  When not,
+     * the load and store methods will return without performing any cache
+     * operations.
+     */
+    bool mInitialized = false;
+
+    /**
+     * "mBlobCache" is the cache in which the key/value blob pairs are stored.  It
+     * is initially NULL, and will be initialized by getBlobCacheLocked the
+     * first time it's needed.
+     * The blob cache contains the Android build number. We treat version mismatches as an empty
+     * cache (logic implemented in BlobCache::unflatten).
+     */
+    std::unique_ptr<FileBlobCache> mBlobCache;
+
+    /**
+     * "mFilename" is the name of the file for storing cache contents in between
+     * program invocations.  It is initialized to an empty string at
+     * construction time, and can be set with the setCacheFilename method.  An
+     * empty string indicates that the cache should not be saved to or restored
+     * from disk.
+     */
+    std::string mFilename;
+
+    /**
+     * "mSavePending" indicates whether or not a deferred save operation is
+     * pending.  Each time a key/value pair is inserted into the cache via
+     * load, a deferred save is initiated if one is not already pending.
+     * This will wait some amount of time and then trigger a save of the cache
+     * contents to disk.
+     */
+    bool mSavePending = false;
+
+    /**
+     *  "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
+     */
+    size_t mObservedBlobValueSize = 20*1024;
+
+    /**
+     *  The time in seconds to wait before saving newly inserted cache entries.
+     */
+    unsigned int mDeferredSaveDelay = 4;
+
+    /**
+     * "mMutex" is the mutex used to prevent concurrent access to the member
+     * variables. It must be locked whenever the member variables are accessed.
+     */
+    mutable std::mutex mMutex;
+
+    /**
+     * "sCache" is the singleton ShaderCache object.
+     */
+    static ShaderCache sCache;
+
+    friend class ShaderCacheTestUtils; //used for unit testing
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 6db57ca..288d039 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -66,14 +66,8 @@
             break;
     }
 
-    /* Ideally, we would call grContext->caps()->isConfigRenderable(...). We
-     * currently can't do that since some devices (i.e. SwiftShader) supports all
-     * the appropriate half float extensions, but only allow the buffer to be read
-     * back as full floats.  We can relax this extension if Skia implements support
-     * for reading back float buffers (skbug.com/6945).
-     */
     if (pixelConfig == kRGBA_half_GrPixelConfig &&
-        !DeviceInfo::get()->extensions().hasRenderableFloatTextures()) {
+            !grContext->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false)) {
         ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
         return CopyResult::DestinationInvalid;
     }
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 6657fe8..91b35c2 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -144,22 +144,25 @@
 // ----------------------------------------------------------------------------
 
 inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint,
-                                         sk_sp<SkColorFilter> colorFilter) {
-    if ((origPaint && origPaint->isAntiAlias()) || colorFilter) {
+                                         sk_sp<SkColorFilter> colorSpaceFilter) {
+    if ((origPaint && origPaint->isAntiAlias()) || colorSpaceFilter) {
         if (origPaint) {
             *tmpPaint = *origPaint;
         }
 
-        sk_sp<SkColorFilter> filter;
-        if (colorFilter && tmpPaint->getColorFilter()) {
-            filter = SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorFilter);
-            LOG_ALWAYS_FATAL_IF(!filter);
-        } else {
-            filter = colorFilter;
+        if (colorSpaceFilter) {
+            if (tmpPaint->getColorFilter()) {
+                tmpPaint->setColorFilter(
+                        SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter));
+            } else {
+                tmpPaint->setColorFilter(colorSpaceFilter);
+            }
+            LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
         }
 
+
+        // disabling AA on bitmap draws matches legacy HWUI behavior
         tmpPaint->setAntiAlias(false);
-        tmpPaint->setColorFilter(filter);
         return tmpPaint;
     } else {
         return origPaint;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index a33b287..c22364b 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -18,6 +18,7 @@
 
 #include "Layer.h"
 #include "RenderThread.h"
+#include "pipeline/skia/ShaderCache.h"
 #include "renderstate/RenderState.h"
 
 #include <GrContextOptions.h>
@@ -127,6 +128,8 @@
         }
         contextOptions->fExecutor = mTaskProcessor.get();
     }
+
+    contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
 }
 
 void CacheManager::trimMemory(TrimMemoryMode mode) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index c117cb8..3e2eeee 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,6 +16,7 @@
 
 #include "RenderThread.h"
 
+#include "pipeline/skia/ShaderCache.h"
 #include "CanvasContext.h"
 #include "EglManager.h"
 #include "OpenGLReadback.h"
@@ -105,6 +106,7 @@
     mRenderState = new RenderState(*this);
     mVkManager = new VulkanManager(*this);
     mCacheManager = new CacheManager(mDisplayInfo);
+    uirenderer::skiapipeline::ShaderCache::get().initShaderDiskCache();
 }
 
 void RenderThread::dumpGraphicsMemory(int fd) {
@@ -139,10 +141,8 @@
             break;
     }
 
-    FILE* file = fdopen(fd, "a");
-    fprintf(file, "\n%s\n", cachesOutput.string());
-    fprintf(file, "\nPipeline=%s\n", pipeline.string());
-    fflush(file);
+    dprintf(fd, "\n%s\n", cachesOutput.string());
+    dprintf(fd, "\nPipeline=%s\n", pipeline.string());
 }
 
 Readback& RenderThread::readback() {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 3272d69..a693e68 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -107,8 +107,11 @@
 
     mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
 
+    GrContextOptions options;
+    options.fDisableDistanceFieldPaths = true;
+    mRenderThread.cacheManager().configureContext(&options);
     mRenderThread.setGrContext(
-            GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get()));
+            GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get(), options));
     DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
 
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
@@ -599,6 +602,7 @@
         mDeviceWaitIdle(mBackendContext->fDevice);
     }
 
+    SkASSERT(surface->mBackbuffers);
     VulkanSurface::BackbufferInfo* backbuffer =
             surface->mBackbuffers + surface->mCurrentBackbufferIndex;
     GrVkImageInfo* imageInfo;
@@ -680,6 +684,7 @@
 }
 
 int VulkanManager::getAge(VulkanSurface* surface) {
+    SkASSERT(surface->mBackbuffers);
     VulkanSurface::BackbufferInfo* backbuffer =
             surface->mBackbuffers + surface->mCurrentBackbufferIndex;
     if (mSwapBehavior == SwapBehavior::Discard ||
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index b9898a7..c319c9e 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -59,11 +59,11 @@
     VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
     VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
 
-    BackbufferInfo* mBackbuffers;
+    BackbufferInfo* mBackbuffers = nullptr;
     uint32_t mCurrentBackbufferIndex;
 
     uint32_t mImageCount;
-    VkImage* mImages;
+    VkImage* mImages = nullptr;
     ImageInfo* mImageInfos;
     uint16_t mCurrentTime = 0;
 };
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 5cd90c5..91022cf 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <gui/Surface.h>
+#include <utils/StrongPointer.h>
+
 #include <string>
 #include <unordered_map>
 
@@ -66,6 +69,8 @@
 
     static std::unordered_map<std::string, Info>& testMap();
     static void registerScene(const Info& info);
+
+    sp<Surface> renderTarget;
 };
 
 }  // namespace test
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
new file mode 100644
index 0000000..9eddc20
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -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.
+ */
+
+#include "TestSceneBase.h"
+#include "renderthread/RenderProxy.h"
+#include "utils/Color.h"
+
+class MagnifierAnimation;
+
+static TestScene::Registrar _Magnifier(TestScene::Info{
+        "magnifier", "A sample magnifier using Readback",
+        TestScene::simpleCreateScene<MagnifierAnimation>});
+
+class MagnifierAnimation : public TestScene {
+public:
+    sp<RenderNode> card;
+    sp<RenderNode> zoomImageView;
+
+    void createContent(int width, int height, Canvas& canvas) override {
+        magnifier = TestUtils::createBitmap(200, 100);
+        SkBitmap temp;
+        magnifier->getSkBitmap(&temp);
+        temp.eraseColor(Color::White);
+        canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
+        card = TestUtils::createNode(
+                0, 0, width, height, [&](RenderProperties& props, Canvas& canvas) {
+                    SkPaint paint;
+                    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+                    paint.setAntiAlias(true);
+                    paint.setTextSize(50);
+
+                    paint.setColor(Color::Black);
+                    TestUtils::drawUtf8ToCanvas(&canvas, "Test string", paint, 10, 400);
+                });
+        canvas.drawRenderNode(card.get());
+        zoomImageView = TestUtils::createNode(
+                100, 100, 500, 300, [&](RenderProperties& props, Canvas& canvas) {
+                    props.setElevation(dp(16));
+                    props.mutableOutline().setRoundRect(0, 0, props.getWidth(), props.getHeight(),
+                                                        dp(6), 1);
+                    props.mutableOutline().setShouldClip(true);
+                    canvas.drawBitmap(*magnifier, 0.0f, 0.0f, (float)magnifier->width(),
+                                      (float)magnifier->height(), 0, 0, (float)props.getWidth(),
+                                      (float)props.getHeight(), nullptr);
+                });
+        canvas.insertReorderBarrier(true);
+        canvas.drawRenderNode(zoomImageView.get());
+        canvas.insertReorderBarrier(false);
+    }
+
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        card->mutateStagingProperties().setTranslationX(curFrame);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        if (renderTarget) {
+            SkBitmap temp;
+            magnifier->getSkBitmap(&temp);
+            constexpr int x = 90;
+            constexpr int y = 325;
+            RenderProxy::copySurfaceInto(renderTarget, x, y, x + magnifier->width(),
+                                         y + magnifier->height(), &temp);
+        }
+    }
+
+    sk_sp<Bitmap> magnifier;
+};
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index b9ce24b..a502116 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -15,7 +15,6 @@
  */
 
 #include "TestSceneBase.h"
-#include "utils/Color.h"
 
 class TextAnimation;
 
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 3eb58a9..9428f53 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -111,8 +111,6 @@
     // Switch to the real display
     gDisplay = getBuiltInDisplay();
 
-    std::unique_ptr<TestScene> scene(info.createScene(opts));
-
     Properties::forceDrawFrame = true;
     TestContext testContext;
     testContext.setRenderOffscreen(opts.renderOffscreen);
@@ -122,6 +120,9 @@
     const int height = gDisplay.h;
     sp<Surface> surface = testContext.surface();
 
+    std::unique_ptr<TestScene> scene(info.createScene(opts));
+    scene->renderTarget = surface;
+
     sp<RenderNode> rootNode = TestUtils::createNode(
             0, 0, width, height, [&scene, width, height](RenderProperties& props, Canvas& canvas) {
                 props.setClipToBounds(false);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index f1ff939..8c0ca5c 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -68,6 +68,7 @@
   --onscreen           Render tests on device screen. By default tests
                        are offscreen rendered
   --benchmark_format   Set output format. Possible values are tabular, json, csv
+  --renderer=TYPE      Sets the render pipeline to use. May be opengl, skiagl, or skiavk
 )");
 }
 
@@ -146,6 +147,20 @@
     return true;
 }
 
+static bool setRenderer(const char* renderer) {
+    if (!strcmp(renderer, "opengl")) {
+        Properties::overrideRenderPipelineType(RenderPipelineType::OpenGL);
+    } else if (!strcmp(renderer, "skiagl")) {
+        Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
+    } else if (!strcmp(renderer, "skiavk")) {
+        Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan);
+    } else {
+        fprintf(stderr, "Unknown format '%s'", renderer);
+        return false;
+    }
+    return true;
+}
+
 // For options that only exist in long-form. Anything in the
 // 0-255 range is reserved for short options (which just use their ASCII value)
 namespace LongOpts {
@@ -158,6 +173,7 @@
     BenchmarkFormat,
     Onscreen,
     Offscreen,
+    Renderer,
 };
 }
 
@@ -172,6 +188,7 @@
         {"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat},
         {"onscreen", no_argument, nullptr, LongOpts::Onscreen},
         {"offscreen", no_argument, nullptr, LongOpts::Offscreen},
+        {"renderer", required_argument, nullptr, LongOpts::Renderer},
         {0, 0, 0, 0}};
 
 static const char* SHORT_OPTIONS = "c:r:h";
@@ -252,6 +269,16 @@
                 }
                 break;
 
+            case LongOpts::Renderer:
+                if (!optarg) {
+                    error = true;
+                    break;
+                }
+                if (!setRenderer(optarg)) {
+                    error = true;
+                }
+                break;
+
             case LongOpts::Onscreen:
                 gOpts.renderOffscreen = false;
                 break;
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
new file mode 100644
index 0000000..43080a9
--- /dev/null
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <dirent.h>
+#include <cutils/properties.h>
+#include <cstdint>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include "pipeline/skia/ShaderCache.h"
+#include "FileBlobCache.h"
+
+using namespace android::uirenderer::skiapipeline;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class ShaderCacheTestUtils {
+public:
+    /**
+     * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
+     * If set to 0, then deferred save is disabled.
+     */
+    static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
+        cache.mDeferredSaveDelay = saveDelay;
+    }
+
+    /**
+     * "terminate" optionally stores the BlobCache on disk and release all in-memory cache.
+     * Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
+     */
+    static void terminate(ShaderCache& cache, bool saveContent) {
+        std::lock_guard<std::mutex> lock(cache.mMutex);
+        if (cache.mInitialized && cache.mBlobCache && saveContent) {
+            cache.mBlobCache->writeToFile();
+        }
+        cache.mBlobCache = NULL;
+    }
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
+
+
+namespace {
+
+std::string getExternalStorageFolder() {
+    return getenv("EXTERNAL_STORAGE");
+}
+
+bool folderExist(const std::string& folderName) {
+    DIR* dir = opendir(folderName.c_str());
+    if (dir) {
+        closedir(dir);
+        return true;
+    }
+    return false;
+}
+
+bool checkShader(const sk_sp<SkData>& shader, const char* program) {
+    sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
+    return shader->size() == shader2->size()
+            && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+}
+
+bool checkShader(const sk_sp<SkData>& shader, std::vector<char>& program) {
+    sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size());
+    return shader->size() == shader2->size()
+            && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+}
+
+void setShader(sk_sp<SkData>& shader, const char* program) {
+    shader = SkData::MakeWithCString(program);
+}
+
+void setShader(sk_sp<SkData>& shader, std::vector<char>& program) {
+    shader = SkData::MakeWithCopy(program.data(), program.size());
+}
+
+
+
+#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
+
+TEST(ShaderCacheTest, testWriteAndRead) {
+    if (!folderExist(getExternalStorageFolder())) {
+        //don't run the test if external storage folder is not available
+        return;
+    }
+    std::string cacheFile1 =  getExternalStorageFolder() + "/shaderCacheTest1";
+    std::string cacheFile2 =  getExternalStorageFolder() + "/shaderCacheTest2";
+
+    //remove any test files from previous test run
+    int deleteFile = remove(cacheFile1.c_str());
+    ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+
+    //read the cache from a file that does not exist
+    ShaderCache::get().setFilename(cacheFile1.c_str());
+    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+    ShaderCache::get().initShaderDiskCache();
+
+    //read a key - should not be found since the cache is empty
+    sk_sp<SkData> outVS;
+    ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
+
+    //write to the in-memory cache without storing on disk and verify we read the same values
+    sk_sp<SkData> inVS;
+    setShader(inVS, "sassas");
+    ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
+    setShader(inVS, "someVS");
+    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+    ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS, "sassas"));
+    ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS, "someVS"));
+
+    //store content to disk and release in-memory cache
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+
+    //change to a file that does not exist and verify load fails
+    ShaderCache::get().setFilename(cacheFile2.c_str());
+    ShaderCache::get().initShaderDiskCache();
+    ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+
+    //load again content from disk from an existing file and check the data is read correctly
+    ShaderCache::get().setFilename(cacheFile1.c_str());
+    ShaderCache::get().initShaderDiskCache();
+    sk_sp<SkData> outVS2;
+    ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS2, "someVS"));
+
+    //change data, store to disk, read back again and verify data has been changed
+    setShader(inVS, "ewData1");
+    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+    ShaderCache::get().initShaderDiskCache();
+    ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS2, "ewData1"));
+
+
+    //write and read big data chunk (50K)
+    size_t dataSize = 50*1024;
+    std::vector<char> dataBuffer(dataSize);
+    for (size_t i = 0; i < dataSize; i++) {
+        dataBuffer[0] = dataSize % 256;
+    }
+    setShader(inVS, dataBuffer);
+    ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+    ShaderCache::get().initShaderDiskCache();
+    ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS2, dataBuffer));
+
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+    remove(cacheFile1.c_str());
+}
+
+}  // namespace
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 20405d3..7afe267 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -244,6 +244,7 @@
         SUPPRESSIBLE_USAGES.put(USAGE_GAME,                              SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
         SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING,    SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT,                         SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+        SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN,                           SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
     }
 
     /**
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index dab7632a..58976ca 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -43,17 +43,16 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Pair;
+import android.util.Slog;
 import android.view.KeyEvent;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -1966,9 +1965,28 @@
      */
     private boolean querySoundEffectsEnabled(int user) {
         return Settings.System.getIntForUser(getContext().getContentResolver(),
-                Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
+                Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0
+                && !areSystemSoundsZenModeBlocked(getContext());
     }
 
+    private boolean areSystemSoundsZenModeBlocked(Context context) {
+        int zenMode = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.ZEN_MODE, 0);
+
+        switch (zenMode) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                return true;
+            case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+                final NotificationManager noMan = (NotificationManager) context
+                        .getSystemService(Context.NOTIFICATION_SERVICE);
+                return (noMan.getNotificationPolicy().priorityCategories
+                        & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0;
+            case Settings.Global.ZEN_MODE_OFF:
+            default:
+                return false;
+        }
+    }
 
     /**
      *  Load Sound effects.
diff --git a/media/java/android/media/BufferingParams.java b/media/java/android/media/BufferingParams.java
index 681271b..521e897 100644
--- a/media/java/android/media/BufferingParams.java
+++ b/media/java/android/media/BufferingParams.java
@@ -26,170 +26,68 @@
 /**
  * Structure for source buffering management params.
  *
- * Used by {@link MediaPlayer#getDefaultBufferingParams()},
- * {@link MediaPlayer#getBufferingParams()} and
+ * Used by {@link MediaPlayer#getBufferingParams()} and
  * {@link MediaPlayer#setBufferingParams(BufferingParams)}
  * to control source buffering behavior.
  *
  * <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering
  * (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer}
- * is playing back source). {@link BufferingParams} includes mode and corresponding
- * watermarks for each stage of source buffering. The watermarks could be either size
- * based (in milliseconds), or time based (in kilobytes) or both, depending on the mode.
+ * is playing back source). {@link BufferingParams} includes corresponding marks for each
+ * stage of source buffering. The marks are time based (in milliseconds).
  *
- * <p>There are 4 buffering modes: {@link #BUFFERING_MODE_NONE},
- * {@link #BUFFERING_MODE_TIME_ONLY}, {@link #BUFFERING_MODE_SIZE_ONLY} and
- * {@link #BUFFERING_MODE_TIME_THEN_SIZE}.
- * {@link MediaPlayer} source component has default buffering modes which can be queried
- * by calling {@link MediaPlayer#getDefaultBufferingParams()}.
- * Users should always use those default modes or their downsized version when trying to
- * change buffering params. For example, {@link #BUFFERING_MODE_TIME_THEN_SIZE} can be
- * downsized to {@link #BUFFERING_MODE_NONE}, {@link #BUFFERING_MODE_TIME_ONLY} or
- * {@link #BUFFERING_MODE_SIZE_ONLY}. But {@link #BUFFERING_MODE_TIME_ONLY} can not be
- * downsized to {@link #BUFFERING_MODE_SIZE_ONLY}.
+ * <p>{@link MediaPlayer} source component has default marks which can be queried by
+ * calling {@link MediaPlayer#getBufferingParams()} before any change is made by
+ * {@link MediaPlayer#setBufferingParams()}.
  * <ul>
- * <li><strong>initial buffering stage:</strong> has one watermark which is used when
- * {@link MediaPlayer} is being prepared. When cached data amount exceeds this watermark,
- * {@link MediaPlayer} is prepared.</li>
- * <li><strong>rebuffering stage:</strong> has two watermarks, low and high, which are
- * used when {@link MediaPlayer} is playing back content.
+ * <li><strong>initial buffering:</strong> initialMarkMs is used when
+ * {@link MediaPlayer} is being prepared. When cached data amount exceeds this mark
+ * {@link MediaPlayer} is prepared. </li>
+ * <li><strong>rebuffering during playback:</strong> resumePlaybackMarkMs is used when
+ * {@link MediaPlayer} is playing back content.
  * <ul>
- * <li> When cached data amount exceeds high watermark, {@link MediaPlayer} will pause
- * buffering. Buffering will resume when cache runs below some limit which could be low
- * watermark or some intermediate value decided by the source component.</li>
- * <li> When cached data amount runs below low watermark, {@link MediaPlayer} will paused
- * playback. Playback will resume when cached data amount exceeds high watermark
- * or reaches end of stream.</li>
- * </ul>
+ * <li> {@link MediaPlayer} has internal mark, namely pausePlaybackMarkMs, to decide when
+ * to pause playback if cached data amount runs low. This internal mark varies based on
+ * type of data source. </li>
+ * <li> When cached data amount exceeds resumePlaybackMarkMs, {@link MediaPlayer} will
+ * resume playback if it has been paused due to low cached data amount. The internal mark
+ * pausePlaybackMarkMs shall be less than resumePlaybackMarkMs. </li>
+ * <li> {@link MediaPlayer} has internal mark, namely pauseRebufferingMarkMs, to decide
+ * when to pause rebuffering. Apparently, this internal mark shall be no less than
+ * resumePlaybackMarkMs. </li>
+ * <li> {@link MediaPlayer} has internal mark, namely resumeRebufferingMarkMs, to decide
+ * when to resume buffering. This internal mark varies based on type of data source. This
+ * mark shall be larger than pausePlaybackMarkMs, and less than pauseRebufferingMarkMs.
+ * </li>
+ * </ul> </li>
  * </ul>
  * <p>Users should use {@link Builder} to change {@link BufferingParams}.
  * @hide
  */
 public final class BufferingParams implements Parcelable {
-    /**
-     * This mode indicates that source buffering is not supported.
-     */
-    public static final int BUFFERING_MODE_NONE = 0;
-    /**
-     * This mode indicates that only time based source buffering is supported. This means
-     * the watermark(s) are time based.
-     */
-    public static final int BUFFERING_MODE_TIME_ONLY = 1;
-    /**
-     * This mode indicates that only size based source buffering is supported. This means
-     * the watermark(s) are size based.
-     */
-    public static final int BUFFERING_MODE_SIZE_ONLY = 2;
-    /**
-     * This mode indicates that both time and size based source buffering are supported,
-     * and time based calculation precedes size based. Size based calculation will be used
-     * only when time information is not available from the source.
-     */
-    public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3;
-
-    /** @hide */
-    @IntDef(
-        value = {
-                BUFFERING_MODE_NONE,
-                BUFFERING_MODE_TIME_ONLY,
-                BUFFERING_MODE_SIZE_ONLY,
-                BUFFERING_MODE_TIME_THEN_SIZE,
-        }
-    )
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface BufferingMode {}
-
-    private static final int BUFFERING_NO_WATERMARK = -1;
+    private static final int BUFFERING_NO_MARK = -1;
 
     // params
-    private int mInitialBufferingMode = BUFFERING_MODE_NONE;
-    private int mRebufferingMode = BUFFERING_MODE_NONE;
+    private int mInitialMarkMs = BUFFERING_NO_MARK;
 
-    private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
-    private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
-
-    private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
-    private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
-    private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
-    private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+    private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
 
     private BufferingParams() {
     }
 
     /**
-     * Return the initial buffering mode used when {@link MediaPlayer} is being prepared.
-     * @return one of the values that can be set in {@link Builder#setInitialBufferingMode(int)}
+     * Return initial buffering mark in milliseconds.
+     * @return initial buffering mark in milliseconds
      */
-    public int getInitialBufferingMode() {
-        return mInitialBufferingMode;
+    public int getInitialMarkMs() {
+        return mInitialMarkMs;
     }
 
     /**
-     * Return the rebuffering mode used when {@link MediaPlayer} is playing back source.
-     * @return one of the values that can be set in {@link Builder#setRebufferingMode(int)}
+     * Return the mark in milliseconds for resuming playback.
+     * @return the mark for resuming playback in milliseconds
      */
-    public int getRebufferingMode() {
-        return mRebufferingMode;
-    }
-
-    /**
-     * Return the time based initial buffering watermark in milliseconds.
-     * It is meaningful only when initial buffering mode obatined from
-     * {@link #getInitialBufferingMode()} is time based.
-     * @return time based initial buffering watermark in milliseconds
-     */
-    public int getInitialBufferingWatermarkMs() {
-        return mInitialWatermarkMs;
-    }
-
-    /**
-     * Return the size based initial buffering watermark in kilobytes.
-     * It is meaningful only when initial buffering mode obatined from
-     * {@link #getInitialBufferingMode()} is size based.
-     * @return size based initial buffering watermark in kilobytes
-     */
-    public int getInitialBufferingWatermarkKB() {
-        return mInitialWatermarkKB;
-    }
-
-    /**
-     * Return the time based low watermark in milliseconds for rebuffering.
-     * It is meaningful only when rebuffering mode obatined from
-     * {@link #getRebufferingMode()} is time based.
-     * @return time based low watermark for rebuffering in milliseconds
-     */
-    public int getRebufferingWatermarkLowMs() {
-        return mRebufferingWatermarkLowMs;
-    }
-
-    /**
-     * Return the time based high watermark in milliseconds for rebuffering.
-     * It is meaningful only when rebuffering mode obatined from
-     * {@link #getRebufferingMode()} is time based.
-     * @return time based high watermark for rebuffering in milliseconds
-     */
-    public int getRebufferingWatermarkHighMs() {
-        return mRebufferingWatermarkHighMs;
-    }
-
-    /**
-     * Return the size based low watermark in kilobytes for rebuffering.
-     * It is meaningful only when rebuffering mode obatined from
-     * {@link #getRebufferingMode()} is size based.
-     * @return size based low watermark for rebuffering in kilobytes
-     */
-    public int getRebufferingWatermarkLowKB() {
-        return mRebufferingWatermarkLowKB;
-    }
-
-    /**
-     * Return the size based high watermark in kilobytes for rebuffering.
-     * It is meaningful only when rebuffering mode obatined from
-     * {@link #getRebufferingMode()} is size based.
-     * @return size based high watermark for rebuffering in kilobytes
-     */
-    public int getRebufferingWatermarkHighKB() {
-        return mRebufferingWatermarkHighKB;
+    public int getResumePlaybackMarkMs() {
+        return mResumePlaybackMarkMs;
     }
 
     /**
@@ -200,27 +98,19 @@
      * <pre class="prettyprint">
      * BufferingParams myParams = mediaplayer.getDefaultBufferingParams();
      * myParams = new BufferingParams.Builder(myParams)
-     *             .setInitialBufferingWatermarkMs(10000)
-     *             .build();
+     *         .setInitialMarkMs(10000)
+     *         .setResumePlaybackMarkMs(15000)
+     *         .build();
      * mediaplayer.setBufferingParams(myParams);
      * </pre>
      */
     public static class Builder {
-        private int mInitialBufferingMode = BUFFERING_MODE_NONE;
-        private int mRebufferingMode = BUFFERING_MODE_NONE;
-
-        private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
-        private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
-
-        private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
-        private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
-        private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
-        private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+        private int mInitialMarkMs = BUFFERING_NO_MARK;
+        private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
 
         /**
          * Constructs a new Builder with the defaults.
-         * By default, both initial buffering mode and rebuffering mode are
-         * {@link BufferingParams#BUFFERING_MODE_NONE}, and all watermarks are -1.
+         * By default, all marks are -1.
          */
         public Builder() {
         }
@@ -231,16 +121,8 @@
          * in the new Builder.
          */
         public Builder(BufferingParams bp) {
-            mInitialBufferingMode = bp.mInitialBufferingMode;
-            mRebufferingMode = bp.mRebufferingMode;
-
-            mInitialWatermarkMs = bp.mInitialWatermarkMs;
-            mInitialWatermarkKB = bp.mInitialWatermarkKB;
-
-            mRebufferingWatermarkLowMs = bp.mRebufferingWatermarkLowMs;
-            mRebufferingWatermarkHighMs = bp.mRebufferingWatermarkHighMs;
-            mRebufferingWatermarkLowKB = bp.mRebufferingWatermarkLowKB;
-            mRebufferingWatermarkHighKB = bp.mRebufferingWatermarkHighKB;
+            mInitialMarkMs = bp.mInitialMarkMs;
+            mResumePlaybackMarkMs = bp.mResumePlaybackMarkMs;
         }
 
         /**
@@ -250,179 +132,37 @@
          * @return a new {@link BufferingParams} object
          */
         public BufferingParams build() {
-            if (isTimeBasedMode(mRebufferingMode)
-                    && mRebufferingWatermarkLowMs > mRebufferingWatermarkHighMs) {
-                throw new IllegalStateException("Illegal watermark:"
-                        + mRebufferingWatermarkLowMs + " : " + mRebufferingWatermarkHighMs);
-            }
-            if (isSizeBasedMode(mRebufferingMode)
-                    && mRebufferingWatermarkLowKB > mRebufferingWatermarkHighKB) {
-                throw new IllegalStateException("Illegal watermark:"
-                        + mRebufferingWatermarkLowKB + " : " + mRebufferingWatermarkHighKB);
-            }
-
             BufferingParams bp = new BufferingParams();
-            bp.mInitialBufferingMode = mInitialBufferingMode;
-            bp.mRebufferingMode = mRebufferingMode;
+            bp.mInitialMarkMs = mInitialMarkMs;
+            bp.mResumePlaybackMarkMs = mResumePlaybackMarkMs;
 
-            bp.mInitialWatermarkMs = mInitialWatermarkMs;
-            bp.mInitialWatermarkKB = mInitialWatermarkKB;
-
-            bp.mRebufferingWatermarkLowMs = mRebufferingWatermarkLowMs;
-            bp.mRebufferingWatermarkHighMs = mRebufferingWatermarkHighMs;
-            bp.mRebufferingWatermarkLowKB = mRebufferingWatermarkLowKB;
-            bp.mRebufferingWatermarkHighKB = mRebufferingWatermarkHighKB;
             return bp;
         }
 
-        private boolean isTimeBasedMode(int mode) {
-            return (mode == BUFFERING_MODE_TIME_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
-        }
-
-        private boolean isSizeBasedMode(int mode) {
-            return (mode == BUFFERING_MODE_SIZE_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
-        }
-
         /**
-         * Sets the initial buffering mode.
-         * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
-         *     {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
-         *     {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
-         *     {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+         * Sets the time based mark in milliseconds for initial buffering.
+         * @param markMs time based mark in milliseconds
          * @return the same Builder instance.
          */
-        public Builder setInitialBufferingMode(@BufferingMode int mode) {
-            switch (mode) {
-                case BUFFERING_MODE_NONE:
-                case BUFFERING_MODE_TIME_ONLY:
-                case BUFFERING_MODE_SIZE_ONLY:
-                case BUFFERING_MODE_TIME_THEN_SIZE:
-                     mInitialBufferingMode = mode;
-                     break;
-                default:
-                     throw new IllegalArgumentException("Illegal buffering mode " + mode);
-            }
+        public Builder setInitialMarkMs(int markMs) {
+            mInitialMarkMs = markMs;
             return this;
         }
 
         /**
-         * Sets the rebuffering mode.
-         * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
-         *     {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
-         *     {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
-         *     {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+         * Sets the time based mark in milliseconds for resuming playback.
+         * @param markMs time based mark in milliseconds for resuming playback
          * @return the same Builder instance.
          */
-        public Builder setRebufferingMode(@BufferingMode int mode) {
-            switch (mode) {
-                case BUFFERING_MODE_NONE:
-                case BUFFERING_MODE_TIME_ONLY:
-                case BUFFERING_MODE_SIZE_ONLY:
-                case BUFFERING_MODE_TIME_THEN_SIZE:
-                     mRebufferingMode = mode;
-                     break;
-                default:
-                     throw new IllegalArgumentException("Illegal buffering mode " + mode);
-            }
-            return this;
-        }
-
-        /**
-         * Sets the time based watermark in milliseconds for initial buffering.
-         * @param watermarkMs time based watermark in milliseconds
-         * @return the same Builder instance.
-         */
-        public Builder setInitialBufferingWatermarkMs(int watermarkMs) {
-            mInitialWatermarkMs = watermarkMs;
-            return this;
-        }
-
-        /**
-         * Sets the size based watermark in kilobytes for initial buffering.
-         * @param watermarkKB size based watermark in kilobytes
-         * @return the same Builder instance.
-         */
-        public Builder setInitialBufferingWatermarkKB(int watermarkKB) {
-            mInitialWatermarkKB = watermarkKB;
-            return this;
-        }
-
-        /**
-         * Sets the time based low watermark in milliseconds for rebuffering.
-         * @param watermarkMs time based low watermark in milliseconds
-         * @return the same Builder instance.
-         */
-        public Builder setRebufferingWatermarkLowMs(int watermarkMs) {
-            mRebufferingWatermarkLowMs = watermarkMs;
-            return this;
-        }
-
-        /**
-         * Sets the time based high watermark in milliseconds for rebuffering.
-         * @param watermarkMs time based high watermark in milliseconds
-         * @return the same Builder instance.
-         */
-        public Builder setRebufferingWatermarkHighMs(int watermarkMs) {
-            mRebufferingWatermarkHighMs = watermarkMs;
-            return this;
-        }
-
-        /**
-         * Sets the size based low watermark in milliseconds for rebuffering.
-         * @param watermarkKB size based low watermark in milliseconds
-         * @return the same Builder instance.
-         */
-        public Builder setRebufferingWatermarkLowKB(int watermarkKB) {
-            mRebufferingWatermarkLowKB = watermarkKB;
-            return this;
-        }
-
-        /**
-         * Sets the size based high watermark in milliseconds for rebuffering.
-         * @param watermarkKB size based high watermark in milliseconds
-         * @return the same Builder instance.
-         */
-        public Builder setRebufferingWatermarkHighKB(int watermarkKB) {
-            mRebufferingWatermarkHighKB = watermarkKB;
-            return this;
-        }
-
-        /**
-         * Sets the time based low and high watermarks in milliseconds for rebuffering.
-         * @param lowWatermarkMs time based low watermark in milliseconds
-         * @param highWatermarkMs time based high watermark in milliseconds
-         * @return the same Builder instance.
-         */
-        public Builder setRebufferingWatermarksMs(int lowWatermarkMs, int highWatermarkMs) {
-            mRebufferingWatermarkLowMs = lowWatermarkMs;
-            mRebufferingWatermarkHighMs = highWatermarkMs;
-            return this;
-        }
-
-        /**
-         * Sets the size based low and high watermarks in kilobytes for rebuffering.
-         * @param lowWatermarkKB size based low watermark in kilobytes
-         * @param highWatermarkKB size based high watermark in kilobytes
-         * @return the same Builder instance.
-         */
-        public Builder setRebufferingWatermarksKB(int lowWatermarkKB, int highWatermarkKB) {
-            mRebufferingWatermarkLowKB = lowWatermarkKB;
-            mRebufferingWatermarkHighKB = highWatermarkKB;
+        public Builder setResumePlaybackMarkMs(int markMs) {
+            mResumePlaybackMarkMs = markMs;
             return this;
         }
     }
 
     private BufferingParams(Parcel in) {
-        mInitialBufferingMode = in.readInt();
-        mRebufferingMode = in.readInt();
-
-        mInitialWatermarkMs = in.readInt();
-        mInitialWatermarkKB = in.readInt();
-
-        mRebufferingWatermarkLowMs = in.readInt();
-        mRebufferingWatermarkHighMs = in.readInt();
-        mRebufferingWatermarkLowKB = in.readInt();
-        mRebufferingWatermarkHighKB = in.readInt();
+        mInitialMarkMs = in.readInt();
+        mResumePlaybackMarkMs = in.readInt();
     }
 
     public static final Parcelable.Creator<BufferingParams> CREATOR =
@@ -446,15 +186,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mInitialBufferingMode);
-        dest.writeInt(mRebufferingMode);
-
-        dest.writeInt(mInitialWatermarkMs);
-        dest.writeInt(mInitialWatermarkKB);
-
-        dest.writeInt(mRebufferingWatermarkLowMs);
-        dest.writeInt(mRebufferingWatermarkHighMs);
-        dest.writeInt(mRebufferingWatermarkLowKB);
-        dest.writeInt(mRebufferingWatermarkHighKB);
+        dest.writeInt(mInitialMarkMs);
+        dest.writeInt(mResumePlaybackMarkMs);
     }
 }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 17af44b..649c091 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1698,21 +1698,9 @@
     public native boolean isPlaying();
 
     /**
-     * Gets the default buffering management params.
-     * Calling it only after {@code setDataSource} has been called.
-     * Each type of data source might have different set of default params.
-     *
-     * @return the default buffering management params supported by the source component.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized, or {@code setDataSource} has not been called.
-     * @hide
-     */
-    @NonNull
-    public native BufferingParams getDefaultBufferingParams();
-
-    /**
      * Gets the current buffering management params used by the source component.
      * Calling it only after {@code setDataSource} has been called.
+     * Each type of data source might have different set of default params.
      *
      * @return the current buffering management params used by the source component.
      * @throws IllegalStateException if the internal player engine has not been
@@ -1727,8 +1715,7 @@
      * The object sets its internal BufferingParams to the input, except that the input is
      * invalid or not supported.
      * Call it only after {@code setDataSource} has been called.
-     * Users should only use supported mode returned by {@link #getDefaultBufferingParams()}
-     * or its downsized version as described in {@link BufferingParams}.
+     * The input is a hint to MediaPlayer.
      *
      * @param params the buffering management params.
      *
diff --git a/media/jni/android_media_BufferingParams.h b/media/jni/android_media_BufferingParams.h
index 24c51f5..b004672 100644
--- a/media/jni/android_media_BufferingParams.h
+++ b/media/jni/android_media_BufferingParams.h
@@ -29,14 +29,8 @@
         jclass      clazz;
         jmethodID   constructID;
 
-        jfieldID    initial_buffering_mode;
-        jfieldID    rebuffering_mode;
-        jfieldID    initial_watermark_ms;
-        jfieldID    initial_watermark_kb;
-        jfieldID    rebuffering_watermark_low_ms;
-        jfieldID    rebuffering_watermark_high_ms;
-        jfieldID    rebuffering_watermark_low_kb;
-        jfieldID    rebuffering_watermark_high_kb;
+        jfieldID    initial_mark_ms;
+        jfieldID    resume_playback_mark_ms;
 
         void init(JNIEnv *env) {
             jclass lclazz = env->FindClass("android/media/BufferingParams");
@@ -51,14 +45,8 @@
 
             constructID = env->GetMethodID(clazz, "<init>", "()V");
 
-            initial_buffering_mode = env->GetFieldID(clazz, "mInitialBufferingMode", "I");
-            rebuffering_mode = env->GetFieldID(clazz, "mRebufferingMode", "I");
-            initial_watermark_ms = env->GetFieldID(clazz, "mInitialWatermarkMs", "I");
-            initial_watermark_kb = env->GetFieldID(clazz, "mInitialWatermarkKB", "I");
-            rebuffering_watermark_low_ms = env->GetFieldID(clazz, "mRebufferingWatermarkLowMs", "I");
-            rebuffering_watermark_high_ms = env->GetFieldID(clazz, "mRebufferingWatermarkHighMs", "I");
-            rebuffering_watermark_low_kb = env->GetFieldID(clazz, "mRebufferingWatermarkLowKB", "I");
-            rebuffering_watermark_high_kb = env->GetFieldID(clazz, "mRebufferingWatermarkHighKB", "I");
+            initial_mark_ms = env->GetFieldID(clazz, "mInitialMarkMs", "I");
+            resume_playback_mark_ms = env->GetFieldID(clazz, "mResumePlaybackMarkMs", "I");
 
             env->DeleteLocalRef(lclazz);
         }
@@ -70,22 +58,10 @@
     };
 
     void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject params) {
-        settings.mInitialBufferingMode =
-            (BufferingMode)env->GetIntField(params, fields.initial_buffering_mode);
-        settings.mRebufferingMode =
-            (BufferingMode)env->GetIntField(params, fields.rebuffering_mode);
-        settings.mInitialWatermarkMs =
-            env->GetIntField(params, fields.initial_watermark_ms);
-        settings.mInitialWatermarkKB =
-            env->GetIntField(params, fields.initial_watermark_kb);
-        settings.mRebufferingWatermarkLowMs =
-            env->GetIntField(params, fields.rebuffering_watermark_low_ms);
-        settings.mRebufferingWatermarkHighMs =
-            env->GetIntField(params, fields.rebuffering_watermark_high_ms);
-        settings.mRebufferingWatermarkLowKB =
-            env->GetIntField(params, fields.rebuffering_watermark_low_kb);
-        settings.mRebufferingWatermarkHighKB =
-            env->GetIntField(params, fields.rebuffering_watermark_high_kb);
+        settings.mInitialMarkMs =
+            env->GetIntField(params, fields.initial_mark_ms);
+        settings.mResumePlaybackMarkMs =
+            env->GetIntField(params, fields.resume_playback_mark_ms);
     }
 
     jobject asJobject(JNIEnv *env, const fields_t& fields) {
@@ -93,14 +69,8 @@
         if (params == NULL) {
             return NULL;
         }
-        env->SetIntField(params, fields.initial_buffering_mode, (jint)settings.mInitialBufferingMode);
-        env->SetIntField(params, fields.rebuffering_mode, (jint)settings.mRebufferingMode);
-        env->SetIntField(params, fields.initial_watermark_ms, (jint)settings.mInitialWatermarkMs);
-        env->SetIntField(params, fields.initial_watermark_kb, (jint)settings.mInitialWatermarkKB);
-        env->SetIntField(params, fields.rebuffering_watermark_low_ms, (jint)settings.mRebufferingWatermarkLowMs);
-        env->SetIntField(params, fields.rebuffering_watermark_high_ms, (jint)settings.mRebufferingWatermarkHighMs);
-        env->SetIntField(params, fields.rebuffering_watermark_low_kb, (jint)settings.mRebufferingWatermarkLowKB);
-        env->SetIntField(params, fields.rebuffering_watermark_high_kb, (jint)settings.mRebufferingWatermarkHighKB);
+        env->SetIntField(params, fields.initial_mark_ms, (jint)settings.mInitialMarkMs);
+        env->SetIntField(params, fields.resume_playback_mark_ms, (jint)settings.mResumePlaybackMarkMs);
 
         return params;
     }
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index cfa3cc36..eda22d5 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -371,25 +371,6 @@
 }
 
 static jobject
-android_media_MediaPlayer_getDefaultBufferingParams(JNIEnv *env, jobject thiz)
-{
-    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return NULL;
-    }
-
-    BufferingParams bp;
-    BufferingSettings &settings = bp.settings;
-    process_media_player_call(
-            env, thiz, mp->getDefaultBufferingSettings(&settings),
-            "java/lang/IllegalStateException", "unexpected error");
-    ALOGV("getDefaultBufferingSettings:{%s}", settings.toString().string());
-
-    return bp.asJobject(env, gBufferingParamsFields);
-}
-
-static jobject
 android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
 {
     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
@@ -1436,7 +1417,6 @@
     {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
     {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
-    {"getDefaultBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getDefaultBufferingParams},
     {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
     {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index fa50943..478297f 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -80,6 +80,9 @@
     // The minimum allowed max packet size is 255 according to the OBEX specification
     public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255;
 
+    // The length of OBEX Byte Sequency Header Id according to the OBEX specification
+    public static final int OBEX_BYTE_SEQ_HEADER_LEN = 0x03;
+
     /**
      * Temporary workaround to be able to push files to Windows 7.
      * TODO: Should be removed as soon as Microsoft updates their driver.
@@ -205,12 +208,15 @@
                     case 0x40:
                         boolean trimTail = true;
                         index++;
-                        length = 0xFF & headerArray[index];
-                        length = length << 8;
-                        index++;
-                        length += 0xFF & headerArray[index];
-                        length -= 3;
-                        index++;
+                        length = ((0xFF & headerArray[index]) << 8) +
+                                 (0xFF & headerArray[index + 1]);
+                        index += 2;
+                        if (length <= OBEX_BYTE_SEQ_HEADER_LEN) {
+                            Log.e(TAG, "Remote sent an OBEX packet with " +
+                                  "incorrect header length = " + length);
+                            break;
+                        }
+                        length -= OBEX_BYTE_SEQ_HEADER_LEN;
                         value = new byte[length];
                         System.arraycopy(headerArray, index, value, 0, length);
                         if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index d90386f..2186169 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -94,7 +94,7 @@
     public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
         final int iconSize = UserIconDrawable.getSizeForList(context);
         if (user.isManagedProfile()) {
-            Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_icon);
+            Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_badge);
             drawable.setBounds(0, 0, iconSize, iconSize);
             return drawable;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index 386c4e0..88f0d2b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -20,14 +20,16 @@
      * Displays preference in this controller.
      */
     public void displayPreference(PreferenceScreen screen) {
+        final String prefKey = getPreferenceKey();
         if (isAvailable()) {
+            setVisible(screen, prefKey, true /* visible */);
             if (this instanceof Preference.OnPreferenceChangeListener) {
-                final Preference preference = screen.findPreference(getPreferenceKey());
+                final Preference preference = screen.findPreference(prefKey);
                 preference.setOnPreferenceChangeListener(
                         (Preference.OnPreferenceChangeListener) this);
             }
         } else {
-            removePreference(screen, getPreferenceKey());
+            setVisible(screen, prefKey, false /* visible */);
         }
     }
 
@@ -59,13 +61,6 @@
     public abstract String getPreferenceKey();
 
     /**
-     * Removes preference from screen.
-     */
-    protected final void removePreference(PreferenceScreen screen, String key) {
-        findAndRemovePreference(screen, key);
-    }
-
-    /**
      * Show/hide a preference.
      */
     protected final void setVisible(PreferenceGroup group, String key, boolean isVisible) {
@@ -74,25 +69,4 @@
             pref.setVisible(isVisible);
         }
     }
-
-    // finds the preference recursively and removes it from its parent
-    private boolean findAndRemovePreference(PreferenceGroup prefGroup, String key) {
-        final int preferenceCount = prefGroup.getPreferenceCount();
-        for (int i = 0; i < preferenceCount; i++) {
-            final Preference preference = prefGroup.getPreference(i);
-            final String curKey = preference.getKey();
-
-            if (curKey != null && curKey.equals(key)) {
-                return prefGroup.removePreference(preference);
-            }
-
-            if (preference instanceof PreferenceGroup) {
-                if (findAndRemovePreference((PreferenceGroup) preference, key)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
index ff7536a..90f14ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
@@ -18,18 +18,20 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.support.annotation.VisibleForTesting;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 import android.text.TextUtils;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 /**
  * Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}.
  */
 public class AbstractSerialNumberPreferenceController extends AbstractPreferenceController {
-    private static final String KEY_SERIAL_NUMBER = "serial_number";
+
+    @VisibleForTesting
+    static final String KEY_SERIAL_NUMBER = "serial_number";
 
     private final String mSerialNumber;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
index 750601d..b27d823 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -57,7 +57,7 @@
             if (userInfo.isManagedProfile()) {
                 mName = context.getString(R.string.managed_user_title);
                 icon = context.getDrawable(
-                    com.android.internal.R.drawable.ic_corp_icon);
+                    com.android.internal.R.drawable.ic_corp_badge);
             } else {
                 mName = userInfo.name;
                 final int userId = userInfo.id;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index c08bc2a..26970e1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -16,17 +16,10 @@
 package com.android.settingslib.core;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceGroup;
-import android.support.v7.preference.PreferenceManager;
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settingslib.TestConfig;
@@ -39,134 +32,58 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
 
 @RunWith(RobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class AbstractPreferenceControllerTest {
 
     @Mock
-    private Context mContext;
-    @Mock
     private PreferenceScreen mScreen;
 
+    private Context mContext;
     private Preference mPreference;
     private TestPrefController mTestPrefController;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mPreference = new Preference(RuntimeEnvironment.application);
+        mContext = RuntimeEnvironment.application;
+        mPreference = new Preference(mContext);
         mPreference.setKey(TestPrefController.KEY_PREF);
+        when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
         mTestPrefController = new TestPrefController(mContext);
     }
 
     @Test
-    public void removeExistingPref_shouldBeRemoved() {
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
-
-        mTestPrefController.removePreference(mScreen, TestPrefController.KEY_PREF);
-
-        verify(mScreen).removePreference(mPreference);
-    }
-
-    @Test
-    public void removeNonExistingPref_shouldNotRemoveAnything() {
-        mTestPrefController.removePreference(mScreen, TestPrefController.KEY_PREF);
-
-        verify(mScreen, never()).removePreference(any(Preference.class));
-    }
-
-    @Test
     public void displayPref_ifAvailable() {
         mTestPrefController.isAvailable = true;
 
         mTestPrefController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
     public void setVisible_prefIsVisible_shouldSetToVisible() {
-        when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
-
         mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, true /* visible */);
+
         assertThat(mPreference.isVisible()).isTrue();
     }
 
     @Test
     public void setVisible_prefNotVisible_shouldSetToInvisible() {
-        when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
-
         mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, false /* visible */);
+
         assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
     public void doNotDisplayPref_ifNotAvailable() {
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
         mTestPrefController.isAvailable = false;
 
         mTestPrefController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
-    }
-
-    @Test
-    public void removePreference_shouldRemoveRecursively() {
-        final Context context = ShadowApplication.getInstance().getApplicationContext();
-        final PreferenceManager preferenceManager = mock(PreferenceManager.class);
-        // Top level
-        PreferenceScreen prefRoot = spy(new PreferenceScreen(context, null));
-        when(prefRoot.getPreferenceManager()).thenReturn(preferenceManager);
-        Preference pref1 = mock(Preference.class);
-        when(pref1.getKey()).thenReturn("key1");
-        PreferenceGroup prefGroup2 = spy(new PreferenceScreen(context, null));
-        when(prefGroup2.getPreferenceManager()).thenReturn(preferenceManager);
-        when(prefGroup2.getKey()).thenReturn("group2");
-        Preference pref3 = mock(Preference.class);
-        when(pref3.getKey()).thenReturn("key3");
-        PreferenceGroup prefGroup4 = spy(new PreferenceScreen(context, null));
-        when(prefGroup4.getPreferenceManager()).thenReturn(preferenceManager);
-        when(prefGroup4.getKey()).thenReturn("group4");
-        prefRoot.addPreference(pref1);
-        prefRoot.addPreference(prefGroup2);
-        prefRoot.addPreference(pref3);
-        prefRoot.addPreference(prefGroup4);
-
-        // 2nd level
-        Preference pref21 = mock(Preference.class);
-        when(pref21.getKey()).thenReturn("key21");
-        Preference pref22 = mock(Preference.class);
-        when(pref22.getKey()).thenReturn("key22");
-        prefGroup2.addPreference(pref21);
-        prefGroup2.addPreference(pref22);
-        PreferenceGroup prefGroup41 = spy(new PreferenceScreen(context, null));
-        when(prefGroup41.getKey()).thenReturn("group41");
-        when(prefGroup41.getPreferenceManager()).thenReturn(preferenceManager);
-        Preference pref42 = mock(Preference.class);
-        when(pref42.getKey()).thenReturn("key42");
-        prefGroup4.addPreference(prefGroup41);
-        prefGroup4.addPreference(pref42);
-
-        // 3rd level
-        Preference pref411 = mock(Preference.class);
-        when(pref411.getKey()).thenReturn("key411");
-        Preference pref412 = mock(Preference.class);
-        when(pref412.getKey()).thenReturn("key412");
-        prefGroup41.addPreference(pref411);
-        prefGroup41.addPreference(pref412);
-
-        mTestPrefController.removePreference(prefRoot, "key1");
-        verify(prefRoot).removePreference(pref1);
-
-        mTestPrefController.removePreference(prefRoot, "key411");
-        verify(prefGroup41).removePreference(pref411);
-
-        mTestPrefController.removePreference(prefRoot, "group41");
-        verify(prefGroup4).removePreference(prefGroup41);
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     private class TestPrefController extends AbstractPreferenceController {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index 32fa01c..aac736a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -17,14 +17,9 @@
 package com.android.settingslib.development;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -67,21 +62,19 @@
         shadowContext.setSystemService(Context.USER_SERVICE, mUserManager);
         mContext = spy(shadowContext.getApplicationContext());
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        mPreference = new SwitchPreference(mContext);
-        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
         mController = new ConcreteEnableAdbPreferenceController(mContext);
+        mPreference = new SwitchPreference(mContext);
         mPreference.setKey(mController.getPreferenceKey());
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
     public void displayPreference_isNotAdmin_shouldRemovePreference() {
         when(mUserManager.isAdminUser()).thenReturn(false);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(mPreference);
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
@@ -90,7 +83,7 @@
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isTrue();
     }
 
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
index 4dbb957..34bbf4f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
@@ -17,13 +17,6 @@
 package com.android.settingslib.deviceinfo;
 
 import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -38,68 +31,67 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class SerialNumberPreferenceControllerTest {
 
-    @Mock(answer = RETURNS_DEEP_STUBS)
-    private Context mContext;
-    @Mock(answer = RETURNS_DEEP_STUBS)
+    @Mock
     private PreferenceScreen mScreen;
 
+    private Context mContext;
+    private Preference mPreference;
     private AbstractSerialNumberPreferenceController mController;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mPreference = new Preference(mContext);
+        mPreference.setKey(AbstractSerialNumberPreferenceController.KEY_SERIAL_NUMBER);
+        when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
     }
 
     @Test
     public void testIsAvaiable_noSerial_shouldReturnFalse() {
-        mController = new ConcreteSerialNumberPreferenceController(mContext, null);
+        mController = new TestPreferenceController(mContext, null);
 
         assertThat(mController.isAvailable()).isFalse();
     }
 
     @Test
-    public void testIsAvaiable_hasSerial_shouldReturnTrue() {
-        mController = new ConcreteSerialNumberPreferenceController(mContext, "123");
+    public void testIsAvailable_hasSerial_shouldReturnTrue() {
+        mController = new TestPreferenceController(mContext, "123");
 
         assertThat(mController.isAvailable()).isTrue();
     }
 
     @Test
     public void testDisplay_noSerial_shouldHidePreference() {
-        final Preference preference = mock(Preference.class);
-        when(mScreen.getPreferenceCount()).thenReturn(1);
-        when(mScreen.getPreference(0)).thenReturn(preference);
-        mController = new ConcreteSerialNumberPreferenceController(mContext, null);
-        when(preference.getKey()).thenReturn(mController.getPreferenceKey());
+        mController = new TestPreferenceController(mContext, null);
 
         mController.displayPreference(mScreen);
 
-        verify(mScreen).removePreference(any(Preference.class));
+        assertThat(mPreference.isVisible()).isFalse();
     }
 
     @Test
     public void testDisplay_hasSerial_shouldSetSummary() {
         final String serial = "123";
-        final Preference preference = mock(Preference.class);
-        when(mScreen.findPreference(anyString())).thenReturn(preference);
 
-        mController = new ConcreteSerialNumberPreferenceController(mContext, serial);
+        mController = new TestPreferenceController(mContext, serial);
         mController.displayPreference(mScreen);
 
-        verify(mScreen, never()).removePreference(any(Preference.class));
-        verify(preference).setSummary(serial);
+        assertThat(mPreference.isVisible()).isTrue();
+        assertThat(mPreference.getSummary()).isEqualTo(serial);
     }
 
-    private static class ConcreteSerialNumberPreferenceController
+    private static class TestPreferenceController
             extends AbstractSerialNumberPreferenceController {
 
-        ConcreteSerialNumberPreferenceController(Context context, String serialNumber) {
+        TestPreferenceController(Context context, String serialNumber) {
             super(context, serialNumber);
         }
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 67fb4d9..41b205b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -37,9 +37,11 @@
         // Global settings
         SettingsState globalSettings = settingsRegistry.getSettingsLocked(
                 SettingsProvider.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
-        long globalSettingsToken = proto.start(SettingsServiceDumpProto.GLOBAL_SETTINGS);
-        dumpProtoGlobalSettingsLocked(globalSettings, proto);
-        proto.end(globalSettingsToken);
+        if (globalSettings != null) {
+            long globalSettingsToken = proto.start(SettingsServiceDumpProto.GLOBAL_SETTINGS);
+            dumpProtoGlobalSettingsLocked(globalSettings, proto);
+            proto.end(globalSettingsToken);
+        }
 
         // Per-user settings
         SparseBooleanArray users = settingsRegistry.getKnownUsersLocked();
@@ -67,19 +69,26 @@
 
         SettingsState secureSettings = settingsRegistry.getSettingsLocked(
                 SettingsProvider.SETTINGS_TYPE_SECURE, user.getIdentifier());
-        long secureSettingsToken = proto.start(UserSettingsProto.SECURE_SETTINGS);
-        dumpProtoSecureSettingsLocked(secureSettings, proto);
-        proto.end(secureSettingsToken);
+        if (secureSettings != null) {
+            long secureSettingsToken = proto.start(UserSettingsProto.SECURE_SETTINGS);
+            dumpProtoSecureSettingsLocked(secureSettings, proto);
+            proto.end(secureSettingsToken);
+        }
 
         SettingsState systemSettings = settingsRegistry.getSettingsLocked(
                 SettingsProvider.SETTINGS_TYPE_SYSTEM, user.getIdentifier());
-        long systemSettingsToken = proto.start(UserSettingsProto.SYSTEM_SETTINGS);
-        dumpProtoSystemSettingsLocked(systemSettings, proto);
-        proto.end(systemSettingsToken);
+        if (systemSettings != null) {
+            long systemSettingsToken = proto.start(UserSettingsProto.SYSTEM_SETTINGS);
+            dumpProtoSystemSettingsLocked(systemSettings, proto);
+            proto.end(systemSettingsToken);
+        }
     }
 
     private static void dumpProtoGlobalSettingsLocked(
             @NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+        s.dumpHistoricalOperations(p, GlobalSettingsProto.HISTORICAL_OPERATIONS);
+
+        // This uses the same order as in Settings.Global.
         dumpSetting(s, p,
                 Settings.Global.ADD_USERS_WHEN_LOCKED,
                 GlobalSettingsProto.ADD_USERS_WHEN_LOCKED);
@@ -114,6 +123,9 @@
                 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
                 GlobalSettingsProto.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
         dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_CLASS_OF_DEVICE,
+                GlobalSettingsProto.BLUETOOTH_CLASS_OF_DEVICE);
+        dumpSetting(s, p,
                 Settings.Global.BLUETOOTH_DISABLED_PROFILES,
                 GlobalSettingsProto.BLUETOOTH_DISABLED_PROFILES);
         dumpSetting(s, p,
@@ -194,6 +206,7 @@
         dumpSetting(s, p,
                 Settings.Global.CDMA_SUBSCRIPTION_MODE,
                 GlobalSettingsProto.CDMA_SUBSCRIPTION_MODE);
+        // Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA intentionally excluded.
         dumpSetting(s, p,
                 Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
                 GlobalSettingsProto.DATA_ACTIVITY_TIMEOUT_MOBILE);
@@ -209,6 +222,10 @@
         dumpSetting(s, p,
                 Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
                 GlobalSettingsProto.FORCE_ALLOW_ON_EXTERNAL);
+        // Settings.Global.DEFAULT_SM_DP_PLUS intentionally excluded.
+        dumpSetting(s, p,
+                Settings.Global.EUICC_PROVISIONED,
+                GlobalSettingsProto.EUICC_PROVISIONED);
         dumpSetting(s, p,
                 Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
                 GlobalSettingsProto.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
@@ -236,6 +253,7 @@
         dumpSetting(s, p,
                 Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
                 GlobalSettingsProto.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
+        // Settings.Global.INSTALL_NON_MARKET_APPS intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Global.HDMI_CONTROL_ENABLED,
                 GlobalSettingsProto.HDMI_CONTROL_ENABLED);
@@ -249,6 +267,21 @@
                 Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                 GlobalSettingsProto.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED);
         dumpSetting(s, p,
+                Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
+                GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
+                GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
+                GlobalSettingsProto.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                GlobalSettingsProto.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+        dumpSetting(s, p,
                 Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
                 GlobalSettingsProto.MHL_INPUT_SWITCHING_ENABLED);
         dumpSetting(s, p,
@@ -279,6 +312,9 @@
                 Settings.Global.NETSTATS_SAMPLE_ENABLED,
                 GlobalSettingsProto.NETSTATS_SAMPLE_ENABLED);
         dumpSetting(s, p,
+                Settings.Global.NETSTATS_AUGMENT_ENABLED,
+                GlobalSettingsProto.NETSTATS_AUGMENT_ENABLED);
+        dumpSetting(s, p,
                 Settings.Global.NETSTATS_DEV_BUCKET_DURATION,
                 GlobalSettingsProto.NETSTATS_DEV_BUCKET_DURATION);
         dumpSetting(s, p,
@@ -420,6 +456,9 @@
                 Settings.Global.TETHER_DUN_APN,
                 GlobalSettingsProto.TETHER_DUN_APN);
         dumpSetting(s, p,
+                Settings.Global.TETHER_OFFLOAD_DISABLED,
+                GlobalSettingsProto.TETHER_OFFLOAD_DISABLED);
+        dumpSetting(s, p,
                 Settings.Global.CARRIER_APP_WHITELIST,
                 GlobalSettingsProto.CARRIER_APP_WHITELIST);
         dumpSetting(s, p,
@@ -450,6 +489,15 @@
                 Settings.Global.NETWORK_AVOID_BAD_WIFI,
                 GlobalSettingsProto.NETWORK_AVOID_BAD_WIFI);
         dumpSetting(s, p,
+                Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE,
+                GlobalSettingsProto.NETWORK_METERED_MULTIPATH_PREFERENCE);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
+                GlobalSettingsProto.NETWORK_WATCHLIST_LAST_REPORT_TIME);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_BADGING_THRESHOLDS,
+                GlobalSettingsProto.WIFI_BADGING_THRESHOLDS);
+        dumpSetting(s, p,
                 Settings.Global.WIFI_DISPLAY_ON,
                 GlobalSettingsProto.WIFI_DISPLAY_ON);
         dumpSetting(s, p,
@@ -489,12 +537,30 @@
                 Settings.Global.WIFI_WAKEUP_ENABLED,
                 GlobalSettingsProto.WIFI_WAKEUP_ENABLED);
         dumpSetting(s, p,
+                Settings.Global.WIFI_WAKEUP_AVAILABLE,
+                GlobalSettingsProto.WIFI_WAKEUP_AVAILABLE);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_SCORING_UI_ENABLED,
+                GlobalSettingsProto.NETWORK_SCORING_UI_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
+                GlobalSettingsProto.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS);
+        dumpSetting(s, p,
                 Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
                 GlobalSettingsProto.NETWORK_RECOMMENDATIONS_ENABLED);
         dumpSetting(s, p,
                 Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE,
                 GlobalSettingsProto.NETWORK_RECOMMENDATIONS_PACKAGE);
         dumpSetting(s, p,
+                Settings.Global.USE_OPEN_WIFI_PACKAGE,
+                GlobalSettingsProto.USE_OPEN_WIFI_PACKAGE);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS,
+                GlobalSettingsProto.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS);
+        dumpSetting(s, p,
+                Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS,
+                GlobalSettingsProto.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS);
+        dumpSetting(s, p,
                 Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
                 GlobalSettingsProto.BLE_SCAN_ALWAYS_AVAILABLE);
         dumpSetting(s, p,
@@ -612,6 +678,12 @@
                 Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                 GlobalSettingsProto.SYS_STORAGE_FULL_THRESHOLD_BYTES);
         dumpSetting(s, p,
+                Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
+                GlobalSettingsProto.SYS_STORAGE_CACHE_PERCENTAGE);
+        dumpSetting(s, p,
+                Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
+                GlobalSettingsProto.SYS_STORAGE_CACHE_MAX_BYTES);
+        dumpSetting(s, p,
                 Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
                 GlobalSettingsProto.SYNC_MAX_RETRY_DELAY_IN_SECONDS);
         dumpSetting(s, p,
@@ -627,6 +699,9 @@
                 Settings.Global.CAPTIVE_PORTAL_MODE,
                 GlobalSettingsProto.CAPTIVE_PORTAL_MODE);
         dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED,
+                GlobalSettingsProto.CAPTIVE_PORTAL_DETECTION_ENABLED);
+        dumpSetting(s, p,
                 Settings.Global.CAPTIVE_PORTAL_SERVER,
                 GlobalSettingsProto.CAPTIVE_PORTAL_SERVER);
         dumpSetting(s, p,
@@ -639,6 +714,9 @@
                 Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL,
                 GlobalSettingsProto.CAPTIVE_PORTAL_FALLBACK_URL);
         dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
+                GlobalSettingsProto.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS);
+        dumpSetting(s, p,
                 Settings.Global.CAPTIVE_PORTAL_USE_HTTPS,
                 GlobalSettingsProto.CAPTIVE_PORTAL_USE_HTTPS);
         dumpSetting(s, p,
@@ -684,6 +762,12 @@
                 Settings.Global.DEFAULT_DNS_SERVER,
                 GlobalSettingsProto.DEFAULT_DNS_SERVER);
         dumpSetting(s, p,
+                Settings.Global.PRIVATE_DNS_MODE,
+                GlobalSettingsProto.PRIVATE_DNS_MODE);
+        dumpSetting(s, p,
+                Settings.Global.PRIVATE_DNS_SPECIFIER,
+                GlobalSettingsProto.PRIVATE_DNS_SPECIFIER);
+        dumpSetting(s, p,
                 Settings.Global.BLUETOOTH_HEADSET_PRIORITY_PREFIX,
                 GlobalSettingsProto.BLUETOOTH_HEADSET_PRIORITY_PREFIX);
         dumpSetting(s, p,
@@ -717,12 +801,27 @@
                 Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX,
                 GlobalSettingsProto.BLUETOOTH_PAN_PRIORITY_PREFIX);
         dumpSetting(s, p,
+                Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
+                GlobalSettingsProto.ACTIVITY_MANAGER_CONSTANTS);
+        dumpSetting(s, p,
                 Settings.Global.DEVICE_IDLE_CONSTANTS,
                 GlobalSettingsProto.DEVICE_IDLE_CONSTANTS);
         dumpSetting(s, p,
+                Settings.Global.BATTERY_SAVER_CONSTANTS,
+                GlobalSettingsProto.BATTERY_SAVER_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.ANOMALY_DETECTION_CONSTANTS,
+                GlobalSettingsProto.ANOMALY_DETECTION_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS,
+                GlobalSettingsProto.ALWAYS_ON_DISPLAY_CONSTANTS);
+        dumpSetting(s, p,
                 Settings.Global.APP_IDLE_CONSTANTS,
                 GlobalSettingsProto.APP_IDLE_CONSTANTS);
         dumpSetting(s, p,
+                Settings.Global.POWER_MANAGER_CONSTANTS,
+                GlobalSettingsProto.POWER_MANAGER_CONSTANTS);
+        dumpSetting(s, p,
                 Settings.Global.ALARM_MANAGER_CONSTANTS,
                 GlobalSettingsProto.ALARM_MANAGER_CONSTANTS);
         dumpSetting(s, p,
@@ -732,6 +831,12 @@
                 Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
                 GlobalSettingsProto.SHORTCUT_MANAGER_CONSTANTS);
         dumpSetting(s, p,
+                Settings.Global.DEVICE_POLICY_CONSTANTS,
+                GlobalSettingsProto.DEVICE_POLICY_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
+                GlobalSettingsProto.TEXT_CLASSIFIER_CONSTANTS);
+        dumpSetting(s, p,
                 Settings.Global.WINDOW_ANIMATION_SCALE,
                 GlobalSettingsProto.WINDOW_ANIMATION_SCALE);
         dumpSetting(s, p,
@@ -764,6 +869,7 @@
         dumpSetting(s, p,
                 Settings.Global.WAIT_FOR_DEBUGGER,
                 GlobalSettingsProto.WAIT_FOR_DEBUGGER);
+        // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Global.LOW_POWER_MODE,
                 GlobalSettingsProto.LOW_POWER_MODE);
@@ -819,6 +925,18 @@
                 Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
                 GlobalSettingsProto.INTENT_FIREWALL_UPDATE_METADATA_URL);
         dumpSetting(s, p,
+                Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
+                GlobalSettingsProto.LANG_ID_UPDATE_CONTENT_URL);
+        dumpSetting(s, p,
+                Settings.Global.LANG_ID_UPDATE_METADATA_URL,
+                GlobalSettingsProto.LANG_ID_UPDATE_METADATA_URL);
+        dumpSetting(s, p,
+                Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
+                GlobalSettingsProto.SMART_SELECTION_UPDATE_CONTENT_URL);
+        dumpSetting(s, p,
+                Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
+                GlobalSettingsProto.SMART_SELECTION_UPDATE_METADATA_URL);
+        dumpSetting(s, p,
                 Settings.Global.SELINUX_STATUS,
                 GlobalSettingsProto.SELINUX_STATUS);
         dumpSetting(s, p,
@@ -882,11 +1000,8 @@
                 Settings.Global.ENABLE_EPHEMERAL_FEATURE,
                 GlobalSettingsProto.ENABLE_EPHEMERAL_FEATURE);
         dumpSetting(s, p,
-                Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
-                GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
-        dumpSetting(s, p,
-                Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
-                GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
+                Settings.Global.INSTANT_APP_DEXOPT_ENABLED,
+                GlobalSettingsProto.INSTANT_APP_DEXOPT_ENABLED);
         dumpSetting(s, p,
                 Settings.Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
                 GlobalSettingsProto.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
@@ -894,6 +1009,12 @@
                 Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                 GlobalSettingsProto.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
         dumpSetting(s, p,
+                Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
+        dumpSetting(s, p,
+                Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+                GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
+        dumpSetting(s, p,
                 Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
                 GlobalSettingsProto.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD);
         dumpSetting(s, p,
@@ -909,12 +1030,30 @@
                 Settings.Global.DEVICE_DEMO_MODE,
                 GlobalSettingsProto.DEVICE_DEMO_MODE);
         dumpSetting(s, p,
+                Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
+                GlobalSettingsProto.NETWORK_ACCESS_TIMEOUT_MS);
+        dumpSetting(s, p,
                 Settings.Global.DATABASE_DOWNGRADE_REASON,
                 GlobalSettingsProto.DATABASE_DOWNGRADE_REASON);
         dumpSetting(s, p,
+                Settings.Global.DATABASE_CREATION_BUILDID,
+                GlobalSettingsProto.DATABASE_CREATION_BUILDID);
+        dumpSetting(s, p,
                 Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
                 GlobalSettingsProto.CONTACTS_DATABASE_WAL_ENABLED);
         dumpSetting(s, p,
+                Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
+                GlobalSettingsProto.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED,
+                GlobalSettingsProto.BACKUP_REFACTORED_SERVICE_DISABLED);
+        dumpSetting(s, p,
+                Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
+                GlobalSettingsProto.EUICC_FACTORY_RESET_TIMEOUT_MILLIS);
+        dumpSetting(s, p,
+                Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD,
+                GlobalSettingsProto.STORAGE_SETTINGS_CLOBBER_THRESHOLD);
+        dumpSetting(s, p,
                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
                 GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
         dumpSetting(s, p,
@@ -932,6 +1071,7 @@
         dumpSetting(s, p,
                 Settings.Global.NEW_CONTACT_AGGREGATOR,
                 GlobalSettingsProto.NEW_CONTACT_AGGREGATOR);
+        // Settings.Global.CONTACT_METADATA_SYNC intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
                 GlobalSettingsProto.CONTACT_METADATA_SYNC_ENABLED);
@@ -942,8 +1082,31 @@
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
                 GlobalSettingsProto.MAX_NOTIFICATION_ENQUEUE_RATE);
         dumpSetting(s, p,
+                Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
+                GlobalSettingsProto.SHOW_NOTIFICATION_CHANNEL_WARNINGS);
+        dumpSetting(s, p,
                 Settings.Global.CELL_ON,
                 GlobalSettingsProto.CELL_ON);
+        dumpSetting(s, p,
+                Settings.Global.SHOW_TEMPERATURE_WARNING,
+                GlobalSettingsProto.SHOW_TEMPERATURE_WARNING);
+        dumpSetting(s, p,
+                Settings.Global.WARNING_TEMPERATURE,
+                GlobalSettingsProto.WARNING_TEMPERATURE);
+        dumpSetting(s, p,
+                Settings.Global.ENABLE_DISKSTATS_LOGGING,
+                GlobalSettingsProto.ENABLE_DISKSTATS_LOGGING);
+        dumpSetting(s, p,
+                Settings.Global.ENABLE_CACHE_QUOTA_CALCULATION,
+                GlobalSettingsProto.ENABLE_CACHE_QUOTA_CALCULATION);
+        dumpSetting(s, p,
+                Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
+                GlobalSettingsProto.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE);
+        // The list of snooze options for notifications. This is encoded as a key=value list,
+        // separated by commas.
+        dumpSetting(s, p,
+                Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+                GlobalSettingsProto.NOTIFICATION_SNOOZE_OPTIONS);
     }
 
     /** Dump a single {@link SettingsState.Setting} to a proto buf */
@@ -966,9 +1129,19 @@
 
     static void dumpProtoSecureSettingsLocked(
             @NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+        s.dumpHistoricalOperations(p, SecureSettingsProto.HISTORICAL_OPERATIONS);
+
+        // This uses the same order as in Settings.Secure.
+
+        // Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED intentionally excluded since it's deprecated.
+        // Settings.Secure.BUGREPORT_IN_POWER_MENU intentionally excluded since it's deprecated.
+        // Settings.Secure.ADB_ENABLED intentionally excluded since it's deprecated.
+        // Settings.Secure.ALLOW_MOCK_LOCATION intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.ANDROID_ID,
                 SecureSettingsProto.ANDROID_ID);
+        // Settings.Secure.BLUETOOTH_ON intentionally excluded since it's deprecated.
+        // Settings.Secure.DATA_ROAMING intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.DEFAULT_INPUT_METHOD,
                 SecureSettingsProto.DEFAULT_INPUT_METHOD);
@@ -987,9 +1160,16 @@
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_SERVICE,
                 SecureSettingsProto.AUTOFILL_SERVICE);
+        // Settings.Secure.DEVICE_PROVISIONED intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.USER_SETUP_COMPLETE,
                 SecureSettingsProto.USER_SETUP_COMPLETE);
+        // Whether the current user has been set up via setup wizard (0 = false, 1 = true). This
+        // value differs from USER_SETUP_COMPLETE in that it can be reset back to 0 in case
+        // SetupWizard has been re-enabled on TV devices.
+        dumpSetting(s, p,
+                Settings.Secure.TV_USER_SETUP_COMPLETE,
+                SecureSettingsProto.TV_USER_SETUP_COMPLETE);
         dumpSetting(s, p,
                 Settings.Secure.COMPLETED_CATEGORY_PREFIX,
                 SecureSettingsProto.COMPLETED_CATEGORY_PREFIX);
@@ -1002,6 +1182,7 @@
         dumpSetting(s, p,
                 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
                 SecureSettingsProto.SHOW_IME_WITH_HARD_KEYBOARD);
+        // Settings.Secure.HTTP_PROXY intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.ALWAYS_ON_VPN_APP,
                 SecureSettingsProto.ALWAYS_ON_VPN_APP);
@@ -1012,17 +1193,33 @@
                 Settings.Secure.INSTALL_NON_MARKET_APPS,
                 SecureSettingsProto.INSTALL_NON_MARKET_APPS);
         dumpSetting(s, p,
+                Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED,
+                SecureSettingsProto.UNKNOWN_SOURCES_DEFAULT_REVERSED);
+        // Settings.Secure.LOCATION_PROVIDERS_ALLOWED intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
                 Settings.Secure.LOCATION_MODE,
                 SecureSettingsProto.LOCATION_MODE);
         dumpSetting(s, p,
                 Settings.Secure.LOCATION_PREVIOUS_MODE,
                 SecureSettingsProto.LOCATION_PREVIOUS_MODE);
+        // Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
                 SecureSettingsProto.LOCK_TO_APP_EXIT_LOCKED);
+        // Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
+        // Settings.Secure.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
+        // Settings.Secure.LOCK_PATTERN_TACTICLE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
                 SecureSettingsProto.LOCK_SCREEN_LOCK_AFTER_TIMEOUT);
+        // Settings.Secure.LOCK_SCREEN_OWNER_INFO intentionally excluded since it's deprecated.
+        // Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS intentionally excluded since it's deprecated.
+        // Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID intentionally excluded since it's deprecated.
+        // Settings.Secure.LOCK_SCREEN_STICKY_APPWIDGET intentionally excluded since it's deprecated.
+        // Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                SecureSettingsProto.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
         dumpSetting(s, p,
                 Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
                 SecureSettingsProto.LOCK_SCREEN_ALLOW_REMOTE_INPUT);
@@ -1032,6 +1229,8 @@
         dumpSetting(s, p,
                 Settings.Secure.TRUST_AGENTS_INITIALIZED,
                 SecureSettingsProto.TRUST_AGENTS_INITIALIZED);
+        // Settings.Secure.LOGGING_ID intentionally excluded since it's deprecated.
+        // Settings.Secure.NETWORK_PREFERENCE intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.PARENTAL_CONTROL_ENABLED,
                 SecureSettingsProto.PARENTAL_CONTROL_ENABLED);
@@ -1044,10 +1243,27 @@
         dumpSetting(s, p,
                 Settings.Secure.SETTINGS_CLASSNAME,
                 SecureSettingsProto.SETTINGS_CLASSNAME);
+        // Settings.Secure.USB_MASS_STORAGE_ENABLED intentionally excluded since it's deprecated.
+        // Settings.Secure.USE_GOOGLE_MAIL intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_ENABLED,
                 SecureSettingsProto.ACCESSIBILITY_ENABLED);
         dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_SHORTCUT_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+                SecureSettingsProto.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+                SecureSettingsProto.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                SecureSettingsProto.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                SecureSettingsProto.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
+        dumpSetting(s, p,
                 Settings.Secure.TOUCH_EXPLORATION_ENABLED,
                 SecureSettingsProto.TOUCH_EXPLORATION_ENABLED);
         dumpSetting(s, p,
@@ -1066,9 +1282,15 @@
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
                 SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
         dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+        dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
                 SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE);
         dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
+                SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE);
+        dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
                 SecureSettingsProto.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
         dumpSetting(s, p,
@@ -1134,6 +1356,7 @@
         dumpSetting(s, p,
                 Settings.Secure.DISPLAY_DENSITY_FORCED,
                 SecureSettingsProto.DISPLAY_DENSITY_FORCED);
+        // Settings.Secure.TTS_USE_DEFAULTS intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.TTS_DEFAULT_RATE,
                 SecureSettingsProto.TTS_DEFAULT_RATE);
@@ -1143,15 +1366,37 @@
         dumpSetting(s, p,
                 Settings.Secure.TTS_DEFAULT_SYNTH,
                 SecureSettingsProto.TTS_DEFAULT_SYNTH);
+        // Settings.Secure.TTS_DEFAULT_LANG intentionally excluded since it's deprecated.
+        // Settings.Secure.TTS_DEFAULT_COUNTRY intentionally excluded since it's deprecated.
+        // Settings.Secure.TTS_DEFAULT_VARIANT intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.TTS_DEFAULT_LOCALE,
                 SecureSettingsProto.TTS_DEFAULT_LOCALE);
         dumpSetting(s, p,
                 Settings.Secure.TTS_ENABLED_PLUGINS,
                 SecureSettingsProto.TTS_ENABLED_PLUGINS);
+        // Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_NUM_OPEN_NETWORKS_KEPT intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_ON intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_AP_COUNT intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_MAX_AP_CHECKS intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_ON intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_WATCH_LIST intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_PING_COUNT intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT intentionally excluded since it's deprecated.
+        // Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
                 SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS);
+        // Settings.Secure.BACKGROUND_DATA intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
                 SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS);
@@ -1179,6 +1424,7 @@
         dumpSetting(s, p,
                 Settings.Secure.LAST_SETUP_SHOWN,
                 SecureSettingsProto.LAST_SETUP_SHOWN);
+        // Settings.Secure.WIFI_IDLE_MS intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
                 SecureSettingsProto.SEARCH_GLOBAL_SEARCH_ACTIVITY);
@@ -1285,6 +1531,9 @@
                 Settings.Secure.DOZE_PULSE_ON_PICK_UP,
                 SecureSettingsProto.DOZE_PULSE_ON_PICK_UP);
         dumpSetting(s, p,
+                Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
+                SecureSettingsProto.DOZE_PULSE_ON_LONG_PRESS);
+        dumpSetting(s, p,
                 Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
                 SecureSettingsProto.DOZE_PULSE_ON_DOUBLE_TAP);
         dumpSetting(s, p,
@@ -1351,6 +1600,9 @@
                 Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
                 SecureSettingsProto.PAYMENT_SERVICE_SEARCH_URI);
         dumpSetting(s, p,
+                Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
+                SecureSettingsProto.AUTOFILL_SERVICE_SEARCH_URI);
+        dumpSetting(s, p,
                 Settings.Secure.SKIP_FIRST_USE_HINTS,
                 SecureSettingsProto.SKIP_FIRST_USE_HINTS);
         dumpSetting(s, p,
@@ -1387,18 +1639,42 @@
                 Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
                 SecureSettingsProto.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED);
         dumpSetting(s, p,
+                Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED,
+                SecureSettingsProto.CAMERA_LIFT_TRIGGER_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_ENABLED,
+                SecureSettingsProto.ASSIST_GESTURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+                SecureSettingsProto.ASSIST_GESTURE_SENSITIVITY);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
+                SecureSettingsProto.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
+                SecureSettingsProto.ASSIST_GESTURE_WAKE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
+                SecureSettingsProto.ASSIST_GESTURE_SETUP_COMPLETE);
+        dumpSetting(s, p,
                 Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
                 SecureSettingsProto.NIGHT_DISPLAY_ACTIVATED);
         dumpSetting(s, p,
                 Settings.Secure.NIGHT_DISPLAY_AUTO_MODE,
                 SecureSettingsProto.NIGHT_DISPLAY_AUTO_MODE);
         dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
+                SecureSettingsProto.NIGHT_DISPLAY_COLOR_TEMPERATURE);
+        dumpSetting(s, p,
                 Settings.Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
                 SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_START_TIME);
         dumpSetting(s, p,
                 Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
                 SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_END_TIME);
         dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+                SecureSettingsProto.NIGHT_DISPLAY_LAST_ACTIVATED_TIME);
+        dumpSetting(s, p,
                 Settings.Secure.ENABLED_VR_LISTENERS,
                 SecureSettingsProto.ENABLED_VR_LISTENERS);
         dumpSetting(s, p,
@@ -1423,6 +1699,9 @@
                 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
                 SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_LAST_RUN);
         dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY,
+                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY);
+        dumpSetting(s, p,
                 Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
                 SecureSettingsProto.SYSTEM_NAVIGATION_KEYS_ENABLED);
         dumpSetting(s, p,
@@ -1438,33 +1717,76 @@
                 Settings.Secure.DEVICE_PAIRED,
                 SecureSettingsProto.DEVICE_PAIRED);
         dumpSetting(s, p,
+                Settings.Secure.PACKAGE_VERIFIER_STATE,
+                SecureSettingsProto.PACKAGE_VERIFIER_STATE);
+        dumpSetting(s, p,
+                Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
+                SecureSettingsProto.CMAS_ADDITIONAL_BROADCAST_PKG);
+        dumpSetting(s, p,
                 Settings.Secure.NOTIFICATION_BADGING,
                 SecureSettingsProto.NOTIFICATION_BADGING);
         dumpSetting(s, p,
+                Settings.Secure.QS_AUTO_ADDED_TILES,
+                SecureSettingsProto.QS_AUTO_ADDED_TILES);
+        dumpSetting(s, p,
+                Settings.Secure.LOCKDOWN_IN_POWER_MENU,
+                SecureSettingsProto.LOCKDOWN_IN_POWER_MENU);
+        dumpSetting(s, p,
                 Settings.Secure.BACKUP_MANAGER_CONSTANTS,
                 SecureSettingsProto.BACKUP_MANAGER_CONSTANTS);
     }
 
     private static void dumpProtoSystemSettingsLocked(
             @NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+        s.dumpHistoricalOperations(p, SystemSettingsProto.HISTORICAL_OPERATIONS);
+
+        // This uses the same order as in Settings.System.
+
+        // Settings.System.STAY_ON_WHILE_PLUGGED_IN intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.END_BUTTON_BEHAVIOR,
                 SystemSettingsProto.END_BUTTON_BEHAVIOR);
         dumpSetting(s, p,
                 Settings.System.ADVANCED_SETTINGS,
                 SystemSettingsProto.ADVANCED_SETTINGS);
+        // Settings.System.AIRPLANE_MODE_ON intentionally excluded since it's deprecated.
+        // Settings.System.RADIO_BLUETOOTH intentionally excluded since it's deprecated.
+        // Settings.System.RADIO_WIFI intentionally excluded since it's deprecated.
+        // Settings.System.RADIO_WIMAX intentionally excluded since it's deprecated.
+        // Settings.System.RADIO_CELL intentionally excluded since it's deprecated.
+        // Settings.System.RADIO_NFC intentionally excluded since it's deprecated.
+        // Settings.System.AIRPLANE_MODE_RADIOS intentionally excluded since it's deprecated.
+        // Settings.System.AIRPLANE_MODE_TOGGLABLE_RADIOS intentionally excluded since it's deprecated.
+        // Settings.System.WIFI_SLEEP_POLICY intentionally excluded since it's deprecated.
+        // Settings.System.MODE_RINGER intentionally excluded since it's deprecated.
+        // Settings.System.WIFI_USE_STATIC_IP intentionally excluded since it's deprecated.
+        // Settings.System.WIFI_STATIC_IP intentionally excluded since it's deprecated.
+        // Settings.System.WIFI_STATIC_GATEWAY intentionally excluded since it's deprecated.
+        // Settings.System.WIFI_STATIC_NETMASK intentionally excluded since it's deprecated.
+        // Settings.System.WIFI_STATIC_DNS1 intentionally excluded since it's deprecated.
+        // Settings.System.WIFI_STATIC_DNS2 intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.BLUETOOTH_DISCOVERABILITY,
                 SystemSettingsProto.BLUETOOTH_DISCOVERABILITY);
         dumpSetting(s, p,
                 Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
                 SystemSettingsProto.BLUETOOTH_DISCOVERABILITY_TIMEOUT);
+        // Settings.System.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
+        // Settings.System.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
+        // Settings.System.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
+        // Settings.System.NEXT_ALARM_FORMATTED intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.FONT_SCALE,
                 SystemSettingsProto.FONT_SCALE);
         dumpSetting(s, p,
                 Settings.System.SYSTEM_LOCALES,
                 SystemSettingsProto.SYSTEM_LOCALES);
+        // Settings.System.DEBUG_APP intentionally excluded since it's deprecated.
+        // Settings.System.WAIT_FOR_DEBUGGER intentionally excluded since it's deprecated.
+        // Settings.System.DIM_SCREEN intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.System.DISPLAY_COLOR_MODE,
+                SystemSettingsProto.DISPLAY_COLOR_MODE);
         dumpSetting(s, p,
                 Settings.System.SCREEN_OFF_TIMEOUT,
                 SystemSettingsProto.SCREEN_OFF_TIMEOUT);
@@ -1480,6 +1802,8 @@
         dumpSetting(s, p,
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
                 SystemSettingsProto.SCREEN_AUTO_BRIGHTNESS_ADJ);
+        // Settings.System.SHOW_PROCESSES intentionally excluded since it's deprecated.
+        // Settings.System.ALWAYS_FINISH_ACTIVITIES intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
                 SystemSettingsProto.MODE_RINGER_STREAMS_AFFECTED);
@@ -1514,11 +1838,15 @@
                 Settings.System.VOLUME_BLUETOOTH_SCO,
                 SystemSettingsProto.VOLUME_BLUETOOTH_SCO);
         dumpSetting(s, p,
+                Settings.System.VOLUME_ACCESSIBILITY,
+                SystemSettingsProto.VOLUME_ACCESSIBILITY);
+        dumpSetting(s, p,
                 Settings.System.VOLUME_MASTER,
                 SystemSettingsProto.VOLUME_MASTER);
         dumpSetting(s, p,
                 Settings.System.MASTER_MONO,
                 SystemSettingsProto.MASTER_MONO);
+        // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.VIBRATE_IN_SILENT,
                 SystemSettingsProto.VIBRATE_IN_SILENT);
@@ -1561,6 +1889,9 @@
         dumpSetting(s, p,
                 Settings.System.SHOW_GTALK_SERVICE_STATUS,
                 SystemSettingsProto.SHOW_GTALK_SERVICE_STATUS);
+        // Settings.System.WALLPAPER_ACTIVITY intentionally excluded since it's deprecated.
+        // Settings.System.AUTO_TIME intentionally excluded since it's deprecated.
+        // Settings.System.AUTO_TIME_ZONE intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.TIME_12_24,
                 SystemSettingsProto.TIME_12_24);
@@ -1570,6 +1901,9 @@
         dumpSetting(s, p,
                 Settings.System.SETUP_WIZARD_HAS_RUN,
                 SystemSettingsProto.SETUP_WIZARD_HAS_RUN);
+        // Settings.System.WINDOW_ANIMATION_SCALE intentionally excluded since it's deprecated.
+        // Settings.System.TRANSITION_ANIMATION_SCALE intentionally excluded since it's deprecated.
+        // Settings.System.ANIMATOR_ANIMATION_SCALE intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.ACCELEROMETER_ROTATION,
                 SystemSettingsProto.ACCELEROMETER_ROTATION);
@@ -1600,6 +1934,7 @@
         dumpSetting(s, p,
                 Settings.System.HAPTIC_FEEDBACK_ENABLED,
                 SystemSettingsProto.HAPTIC_FEEDBACK_ENABLED);
+        // Settings.System.SHOW_WEB_SUGGESTIONS intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.NOTIFICATION_LIGHT_PULSE,
                 SystemSettingsProto.NOTIFICATION_LIGHT_PULSE);
@@ -1612,12 +1947,21 @@
         dumpSetting(s, p,
                 Settings.System.WINDOW_ORIENTATION_LISTENER_LOG,
                 SystemSettingsProto.WINDOW_ORIENTATION_LISTENER_LOG);
+        // Settings.System.POWER_SOUNDS_ENABLED intentionally excluded since it's deprecated.
+        // Settings.System.DOCK_SOUNDS_ENABLED intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
                 SystemSettingsProto.LOCKSCREEN_SOUNDS_ENABLED);
         dumpSetting(s, p,
                 Settings.System.LOCKSCREEN_DISABLED,
                 SystemSettingsProto.LOCKSCREEN_DISABLED);
+        // Settings.System.LOW_BATTERY_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.DESK_DOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.DESK_UNDOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.CAR_DOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.CAR_UNDOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.LOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.UNLOCK_SOUND intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.SIP_RECEIVE_CALLS,
                 SystemSettingsProto.SIP_RECEIVE_CALLS);
@@ -1630,6 +1974,7 @@
         dumpSetting(s, p,
                 Settings.System.SIP_ADDRESS_ONLY,
                 SystemSettingsProto.SIP_ADDRESS_ONLY);
+        // Settings.System.SIP_ASK_ME_EACH_TIME intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.System.POINTER_SPEED,
                 SystemSettingsProto.POINTER_SPEED);
@@ -1640,7 +1985,12 @@
                 Settings.System.EGG_MODE,
                 SystemSettingsProto.EGG_MODE);
         dumpSetting(s, p,
+                Settings.System.SHOW_BATTERY_PERCENT,
+                SystemSettingsProto.SHOW_BATTERY_PERCENT);
+        dumpSetting(s, p,
                 Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
                 SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+        // The rest of the settings were moved to Settings.Secure, and are thus excluded here since
+        // they're deprecated from Settings.System.
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 36f9b84..258c96c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.backup.BackupManager;
@@ -657,7 +658,6 @@
 
         synchronized (mLock) {
             SettingsProtoDumpUtil.dumpProtoLocked(mSettingsRegistry, proto);
-
         }
 
         proto.flush();
@@ -2284,6 +2284,7 @@
             return users;
         }
 
+        @Nullable
         public SettingsState getSettingsLocked(int type, int userId) {
             final int key = makeKey(type, userId);
             return peekSettingsStateLocked(key);
@@ -2578,6 +2579,7 @@
             ssaidSettings.deleteSettingLocked(Integer.toString(uid));
         }
 
+        @Nullable
         private SettingsState peekSettingsStateLocked(int key) {
             SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState != null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4151ada..f901bca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -434,8 +434,9 @@
      * Dump historical operations as a proto buf.
      *
      * @param proto The proto buf stream to dump to
+     * @param fieldId The repeated field ID to use to save an operation to.
      */
-    void dumpProtoHistoricalOperations(@NonNull ProtoOutputStream proto) {
+    void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) {
         synchronized (mLock) {
             if (mHistoricalOperations == null) {
                 return;
@@ -448,7 +449,8 @@
                     index = operationCount + index;
                 }
                 HistoricalOperation operation = mHistoricalOperations.get(index);
-                long settingsOperationToken = proto.start(GlobalSettingsProto.HISTORICAL_OP);
+
+                final long token = proto.start(fieldId);
                 proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp);
                 proto.write(SettingsOperationProto.OPERATION, operation.mOperation);
                 if (operation.mSetting != null) {
@@ -457,7 +459,7 @@
                     // add is what the current data is).
                     proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName());
                 }
-                proto.end(settingsOperationToken);
+                proto.end(token);
             }
         }
     }
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 3d9a2dc..fac254a 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -13,30 +13,36 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout
+<com.android.systemui.HardwareUiLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/volume_dialog"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginBottom="@dimen/volume_dialog_margin_bottom"
-    android:background="@drawable/volume_dialog_background"
-    android:paddingTop="@dimen/volume_dialog_padding_top"
+    android:layout_height="match_parent"
     android:theme="@style/qs_theme"
-    android:translationZ="4dp" >
-
-    <LinearLayout
-        android:id="@+id/volume_dialog_content"
-        android:layout_width="match_parent"
+    android:clipChildren="false" >
+    <RelativeLayout
+        android:id="@+id/volume_dialog"
+        android:layout_width="@dimen/volume_dialog_panel_width"
         android:layout_height="wrap_content"
-        android:orientation="vertical" >
+        android:layout_gravity="center_vertical|end"
+        android:paddingTop="@dimen/volume_row_padding_bottom"
+        android:layout_margin="12dp"
+        android:background="?android:attr/actionBarItemBackground"
+        android:translationZ="8dp" >
 
         <LinearLayout
-            android:id="@+id/volume_dialog_rows"
+            android:id="@+id/volume_dialog_content"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical" >
-            <!-- volume rows added and removed here! :-) -->
-        </LinearLayout>
-    </LinearLayout>
 
-</RelativeLayout>
+            <LinearLayout
+                android:id="@+id/volume_dialog_rows"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical" >
+                <!-- volume rows added and removed here! :-) -->
+            </LinearLayout>
+        </LinearLayout>
+
+    </RelativeLayout>
+</com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 7328d05..bf76e78 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -19,8 +19,8 @@
     android:layout_height="@dimen/volume_row_height"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:orientation="vertical"
-    android:paddingBottom="@dimen/volume_row_padding_bottom" >
+    android:theme="@style/qs_theme"
+    android:orientation="vertical" >
 
     <TextView
         android:id="@+id/volume_row_header"
@@ -28,7 +28,8 @@
         android:layout_height="wrap_content"
         android:ellipsize="end"
         android:maxLines="1"
-        android:textAppearance="@style/TextAppearance.Volume.Header"
+        android:textColor="?android:attr/colorControlNormal"
+        android:textAppearance="?android:attr/textAppearanceSmall"
         android:paddingStart="@dimen/volume_row_header_padding_start" />
 
     <LinearLayout
@@ -53,4 +54,9 @@
                 android:paddingStart="@dimen/volume_row_slider_padding_start"/>
     </LinearLayout>
 
+    <Space
+        android:id="@+id/spacer"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/volume_row_padding_bottom"/>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c678111f..f0bad2a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -240,7 +240,7 @@
     <!-- The width of the panel that holds the quick settings. -->
     <dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
 
-    <dimen name="volume_dialog_panel_width">@dimen/standard_notification_panel_width</dimen>
+    <dimen name="volume_dialog_panel_width">315dp</dimen>
 
     <!-- Gravity for the notification panel -->
     <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1536b64..b132160 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -124,16 +124,16 @@
     <string name="status_bar_use_physical_keyboard">Physical keyboard</string>
 
     <!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] -->
-    <string name="usb_device_permission_prompt">Allow the app <xliff:g id="application">%1$s</xliff:g> to access the USB device?</string>
+    <string name="usb_device_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_device">%2$s</xliff:g>?</string>
 
     <!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] -->
-    <string name="usb_accessory_permission_prompt">Allow the app <xliff:g id="application">%1$s</xliff:g> to access the USB accessory?</string>
+    <string name="usb_accessory_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
 
     <!-- Prompt for the USB device confirm dialog [CHAR LIMIT=80] -->
-    <string name="usb_device_confirm_prompt">Open <xliff:g id="activity">%1$s</xliff:g> when this USB device is connected?</string>
+    <string name="usb_device_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_device">%2$s</xliff:g>?</string>
 
     <!-- Prompt for the USB accessory confirm dialog [CHAR LIMIT=80] -->
-    <string name="usb_accessory_confirm_prompt">Open <xliff:g id="activity">%1$s</xliff:g> when this USB accessory is connected?</string>
+    <string name="usb_accessory_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
 
     <!-- Prompt for the USB accessory URI dialog [CHAR LIMIT=80] -->
     <string name="usb_accessory_uri_prompt">No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="url">%1$s</xliff:g></string>
@@ -145,10 +145,10 @@
     <string name="label_view">View</string>
 
     <!-- Checkbox label for USB device dialogs.  [CHAR LIMIT=50] -->
-    <string name="always_use_device">Use by default for this USB device</string>
+    <string name="always_use_device">Always open <xliff:g id="application">%1$s</xliff:g> when <xliff:g id="usb_device">%2$s</xliff:g> is connected</string>
 
-    <!-- Checkbox label for USB accessory dialogs.  [CHAR LIMIT=50] -->
-    <string name="always_use_accessory">Use by default for this USB accessory</string>
+    <!-- Checkbox label for USB accessory dialogs.  [CHAR LIMIT=50]-->
+    <string name="always_use_accessory">Always open <xliff:g id="application">%1$s</xliff:g> when <xliff:g id="usb_accessory">%2$s</xliff:g> is connected</string>
 
     <!-- Title of confirmation dialog for USB debugging -->
     <string name="usb_debugging_title">Allow USB debugging?</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
similarity index 82%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
rename to packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 8cf3be8..173a90a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.shared.recents;
 
 import android.view.MotionEvent;
+import com.android.systemui.shared.recents.ISystemUiProxy;
 
 oneway interface IOverviewProxy {
+    void onBind(in ISystemUiProxy sysUiProxy);
     void onMotionEvent(in MotionEvent event);
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
new file mode 100644
index 0000000..d82b010
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.recents;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+
+/**
+ * Temporary callbacks into SystemUI.
+ */
+interface ISystemUiProxy {
+
+    /**
+     * Proxies SurfaceControl.screenshot().
+     */
+    Bitmap screenshot(in Rect sourceCrop, int width, int height,
+                      int minLayer, int maxLayer, boolean useIdentityTransform,
+                      int rotation);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 62a9319..e7e70af 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -26,7 +26,7 @@
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -205,8 +205,8 @@
         mProviders.put(BatteryController.class, () ->
                 new BatteryControllerImpl(mContext));
 
-        mProviders.put(NightDisplayController.class, () ->
-                new NightDisplayController(mContext));
+        mProviders.put(ColorDisplayController.class, () ->
+                new ColorDisplayController(mContext));
 
         mProviders.put(ManagedProfileController.class, () ->
                 new ManagedProfileControllerImpl(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 528e242..fc1c84a 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -16,36 +16,75 @@
 
 package com.android.systemui;
 
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.PatternMatcher;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
-import com.android.systemui.shared.recents.model.IOverviewProxy;
+import android.view.SurfaceControl;
+
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.statusbar.policy.CallbackController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Class to send information from overview to launcher with a binder.
  */
-public class OverviewProxyService {
+public class OverviewProxyService implements CallbackController<OverviewProxyListener> {
 
     private static final String TAG = "OverviewProxyService";
     private static final long BACKOFF_MILLIS = 5000;
 
     private final Context mContext;
     private final Handler mHandler;
-    private final Runnable mConnectionRunnable = this::startConnectionToCurrentUser;
+    private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
+    private final ComponentName mLauncherComponentName;
     private final DeviceProvisionedController mDeviceProvisionedController
             = Dependency.get(DeviceProvisionedController.class);
+    private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
 
     private IOverviewProxy mOverviewProxy;
     private int mConnectionBackoffAttempts;
 
+    private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
+        public Bitmap screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+                boolean useIdentityTransform, int rotation) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                return SurfaceControl.screenshot(sourceCrop, width, height, minLayer, maxLayer,
+                        useIdentityTransform, rotation);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Reconnect immediately, instead of waiting for resume to arrive.
+            startConnectionToCurrentUser();
+        }
+    };
+
     private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -58,6 +97,12 @@
                 } catch (RemoteException e) {
                     Log.e(TAG, "Lost connection to launcher service", e);
                 }
+                try {
+                    mOverviewProxy.onBind(mSysUiProxy);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to call onBind()", e);
+                }
+                notifyConnectionChanged();
             }
         }
 
@@ -72,33 +117,46 @@
             @Override
             public void onUserSetupChanged() {
                 if (mDeviceProvisionedController.isCurrentUserSetup()) {
-                    startConnectionToCurrentUser();
+                    internalConnectToCurrentUser();
                 }
             }
 
             @Override
             public void onUserSwitched() {
                 mConnectionBackoffAttempts = 0;
-                startConnectionToCurrentUser();
+                internalConnectToCurrentUser();
             }
         };
 
     // This is the death handler for the binder from the launcher service
-    private final IBinder.DeathRecipient mOverviewServiceDeathRcpt = new IBinder.DeathRecipient() {
-        @Override
-        public void binderDied() {
-            mOverviewProxy = null;
-        }
-    };
+    private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
+            = this::startConnectionToCurrentUser;
 
     public OverviewProxyService(Context context) {
         mContext = context;
         mHandler = new Handler();
         mConnectionBackoffAttempts = 0;
+        mLauncherComponentName = ComponentName
+                .unflattenFromString(context.getString(R.string.config_overviewServiceComponent));
         mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
+
+        // Listen for the package update changes.
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addDataScheme("package");
+        filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(),
+                PatternMatcher.PATTERN_LITERAL);
+        mContext.registerReceiver(mLauncherAddedReceiver, filter);
     }
 
     public void startConnectionToCurrentUser() {
+        if (mHandler.getLooper() != Looper.myLooper()) {
+            mHandler.post(mConnectionRunnable);
+        } else {
+            internalConnectToCurrentUser();
+        }
+    }
+
+    private void internalConnectToCurrentUser() {
         disconnectFromLauncherService();
 
         // If user has not setup yet or already connected, do not try to connect
@@ -107,8 +165,7 @@
         }
         mHandler.removeCallbacks(mConnectionRunnable);
         Intent launcherServiceIntent = new Intent();
-        launcherServiceIntent.setComponent(ComponentName.unflattenFromString(
-                mContext.getString(R.string.config_overviewServiceComponent)));
+        launcherServiceIntent.setComponent(mLauncherComponentName);
         boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
                 mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
                 UserHandle.getUserHandleForUid(mDeviceProvisionedController.getCurrentUser()));
@@ -120,14 +177,37 @@
         }
     }
 
+    @Override
+    public void addCallback(OverviewProxyListener listener) {
+        mConnectionCallbacks.add(listener);
+        listener.onConnectionChanged(mOverviewProxy != null);
+    }
+
+    @Override
+    public void removeCallback(OverviewProxyListener listener) {
+        mConnectionCallbacks.remove(listener);
+    }
+
     public IOverviewProxy getProxy() {
         return mOverviewProxy;
     }
 
     private void disconnectFromLauncherService() {
         if (mOverviewProxy != null) {
+            mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
             mContext.unbindService(mOverviewServiceConnection);
             mOverviewProxy = null;
+            notifyConnectionChanged();
         }
     }
+
+    private void notifyConnectionChanged() {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
+        }
+    }
+
+    public interface OverviewProxyListener {
+        void onConnectionChanged(boolean isConnected);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d82f9cd..00e8b1a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -80,7 +80,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
+import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 28adca9..a35ba9f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -28,6 +28,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
@@ -1691,6 +1692,8 @@
             mUiOffloadThread.submit(() -> {
                 // If the stream is muted, don't play the sound
                 if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;
+                // If DND blocks the sound, don't play the sound
+                if (areSystemSoundsZenModeBlocked(mContext)) return;
 
                 int id = mLockSounds.play(soundId,
                         mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
@@ -1702,6 +1705,25 @@
         }
     }
 
+    private boolean areSystemSoundsZenModeBlocked(Context context) {
+        int zenMode = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.ZEN_MODE, 0);
+
+        switch (zenMode) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                return true;
+            case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+                final NotificationManager noMan = (NotificationManager) context
+                        .getSystemService(Context.NOTIFICATION_SERVICE);
+                return (noMan.getNotificationPolicy().priorityCategories
+                        & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0;
+            case Settings.Global.ZEN_MODE_OFF:
+            default:
+                return false;
+        }
+    }
+
     private void playTrustedSound() {
         playSound(mTrustedSoundId);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 532ead1..5ffd785 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -338,7 +338,9 @@
 
         final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
 
-        mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
+
+        mMultiUserSwitch.setVisibility(mExpanded
+                && UserManager.get(mContext).isUserSwitcherEnabled()
                 ? View.VISIBLE : View.INVISIBLE);
 
         mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 4c20361..763ffc6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -22,8 +22,7 @@
 import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
-import com.android.internal.app.NightDisplayController;
-import com.android.internal.logging.MetricsLogger;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSHost;
@@ -31,19 +30,19 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
 public class NightDisplayTile extends QSTileImpl<BooleanState>
-        implements NightDisplayController.Callback {
+        implements ColorDisplayController.Callback {
 
-    private NightDisplayController mController;
+    private ColorDisplayController mController;
     private boolean mIsListening;
 
     public NightDisplayTile(QSHost host) {
         super(host);
-        mController = new NightDisplayController(mContext, ActivityManager.getCurrentUser());
+        mController = new ColorDisplayController(mContext, ActivityManager.getCurrentUser());
     }
 
     @Override
     public boolean isAvailable() {
-        return NightDisplayController.isAvailable(mContext);
+        return ColorDisplayController.isAvailable(mContext);
     }
 
     @Override
@@ -65,7 +64,7 @@
         }
 
         // Make a new controller for the new user.
-        mController = new NightDisplayController(mContext, newUserId);
+        mController = new ColorDisplayController(mContext, newUserId);
         if (mIsListening) {
             mController.setListener(this);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a436e17..5eb315e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -208,6 +208,8 @@
      *
      * @param isHomeStackVisible if provided, will return whether the home stack is visible
      *                           regardless of the recents visibility
+     *
+     * TODO(winsonc): Refactor this check to just use the recents activity lifecycle
      */
     public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
         if (mIam == null) return false;
@@ -222,13 +224,13 @@
                 final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration;
                 final int activityType = winConfig.getActivityType();
                 final int windowingMode = winConfig.getWindowingMode();
-                if (activityType == ACTIVITY_TYPE_HOME) {
+                if (homeStackInfo == null && activityType == ACTIVITY_TYPE_HOME) {
                     homeStackInfo = stackInfo;
-                } else if (activityType == ACTIVITY_TYPE_STANDARD
+                } else if (fullscreenStackInfo == null && activityType == ACTIVITY_TYPE_STANDARD
                         && (windowingMode == WINDOWING_MODE_FULLSCREEN
                             || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
                     fullscreenStackInfo = stackInfo;
-                } else if (activityType == ACTIVITY_TYPE_RECENTS) {
+                } else if (recentsStackInfo == null && activityType == ACTIVITY_TYPE_RECENTS) {
                     recentsStackInfo = stackInfo;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 54d622b..c4024a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -18,46 +18,21 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.app.INotificationManager;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.Switch;
-import android.widget.TextView;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.Utils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
-import java.util.Set;
-
 /**
  * The guts of a notification revealed when performing a long press.
  */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
new file mode 100644
index 0000000..b585bdf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.INotificationManager;
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
+ * closing guts, and keeping track of the currently exposed notification guts.
+ */
+public class NotificationGutsManager implements Dumpable {
+    private static final String TAG = "NotificationGutsManager";
+
+    // Must match constant in Settings. Used to highlight preferences when linking to Settings.
+    private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final Set<String> mNonBlockablePkgs;
+    private final NotificationPresenter mPresenter;
+    // TODO: Create NotificationListContainer interface and use it instead of
+    // NotificationStackScrollLayout here
+    private final NotificationStackScrollLayout mStackScroller;
+    private final Context mContext;
+    private final AccessibilityManager mAccessibilityManager;
+    // which notification is currently being longpress-examined by the user
+    private NotificationGuts mNotificationGutsExposed;
+    private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
+    private final NotificationInfo.CheckSaveListener mCheckSaveListener;
+    private String mKeyToRemoveOnGutsClosed;
+
+    public NotificationGutsManager(
+            NotificationPresenter presenter,
+            NotificationStackScrollLayout stackScroller,
+            NotificationInfo.CheckSaveListener checkSaveListener,
+            Context context) {
+        mPresenter = presenter;
+        mStackScroller = stackScroller;
+        mCheckSaveListener = checkSaveListener;
+        mContext = context;
+        Resources res = context.getResources();
+
+        mNonBlockablePkgs = new HashSet<>();
+        Collections.addAll(mNonBlockablePkgs, res.getStringArray(
+                com.android.internal.R.array.config_nonBlockableNotificationPackages));
+
+        mAccessibilityManager = (AccessibilityManager)
+                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+    }
+
+    public String getKeyToRemoveOnGutsClosed() {
+        return mKeyToRemoveOnGutsClosed;
+    }
+
+    public void setKeyToRemoveOnGutsClosed(String keyToRemoveOnGutsClosed) {
+        mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
+    }
+
+    private void saveAndCloseNotificationMenu(
+            ExpandableNotificationRow row, NotificationGuts guts, View done) {
+        guts.resetFalsingCheck();
+        int[] rowLocation = new int[2];
+        int[] doneLocation = new int[2];
+        row.getLocationOnScreen(rowLocation);
+        done.getLocationOnScreen(doneLocation);
+
+        final int centerX = done.getWidth() / 2;
+        final int centerY = done.getHeight() / 2;
+        final int x = doneLocation[0] - rowLocation[0] + centerX;
+        final int y = doneLocation[1] - rowLocation[1] + centerY;
+        closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+                true /* removeControls */, x, y, true /* resetMenu */);
+    }
+
+    /**
+     * Sends an intent to open the notification settings for a particular package and optional
+     * channel.
+     */
+    private void startAppNotificationSettingsActivity(String packageName, final int appUid,
+            final NotificationChannel channel) {
+        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+        intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
+        intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+        if (channel != null) {
+            intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
+        }
+        mPresenter.startNotificationGutsIntent(intent, appUid);
+    }
+
+    public void bindGuts(final ExpandableNotificationRow row) {
+        bindGuts(row, mGutsMenuItem);
+    }
+
+    private void bindGuts(final ExpandableNotificationRow row,
+            NotificationMenuRowPlugin.MenuItem item) {
+        row.inflateGuts();
+        row.setGutsView(item);
+        final StatusBarNotification sbn = row.getStatusBarNotification();
+        row.setTag(sbn.getPackageName());
+        final NotificationGuts guts = row.getGuts();
+        guts.setClosedListener((NotificationGuts g) -> {
+            if (!g.willBeRemoved() && !row.isRemoved()) {
+                mStackScroller.onHeightChanged(
+                        row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
+            }
+            if (mNotificationGutsExposed == g) {
+                mNotificationGutsExposed = null;
+                mGutsMenuItem = null;
+            }
+            String key = sbn.getKey();
+            if (key.equals(mKeyToRemoveOnGutsClosed)) {
+                mKeyToRemoveOnGutsClosed = null;
+                mPresenter.removeNotification(key, mPresenter.getLatestRankingMap());
+            }
+        });
+
+        View gutsView = item.getGutsView();
+        if (gutsView instanceof NotificationSnooze) {
+            NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
+            snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
+            snoozeGuts.setStatusBarNotification(sbn);
+            snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
+            guts.setHeightChangedListener((NotificationGuts g) -> {
+                mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
+            });
+        }
+
+        if (gutsView instanceof NotificationInfo) {
+            final UserHandle userHandle = sbn.getUser();
+            PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+                    userHandle.getIdentifier());
+            final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
+                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+            final String pkg = sbn.getPackageName();
+            NotificationInfo info = (NotificationInfo) gutsView;
+            // Settings link is only valid for notifications that specify a user, unless this is the
+            // system user.
+            NotificationInfo.OnSettingsClickListener onSettingsClick = null;
+            if (!userHandle.equals(UserHandle.ALL)
+                    || mPresenter.getCurrentUserId() == UserHandle.USER_SYSTEM) {
+                onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
+                    mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+                    guts.resetFalsingCheck();
+                    startAppNotificationSettingsActivity(pkg, appUid, channel);
+                };
+            }
+            final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
+                    Intent intent) -> {
+                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
+                guts.resetFalsingCheck();
+                mPresenter.startNotificationGutsIntent(intent, sbn.getUid());
+            };
+            final View.OnClickListener onDoneClick = (View v) -> {
+                saveAndCloseNotificationMenu(row, guts, v);
+            };
+
+            ArraySet<NotificationChannel> channels = new ArraySet<>();
+            channels.add(row.getEntry().channel);
+            if (row.isSummaryWithChildren()) {
+                // If this is a summary, then add in the children notification channels for the
+                // same user and pkg.
+                final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
+                final int numChildren = childrenRows.size();
+                for (int i = 0; i < numChildren; i++) {
+                    final ExpandableNotificationRow childRow = childrenRows.get(i);
+                    final NotificationChannel childChannel = childRow.getEntry().channel;
+                    final StatusBarNotification childSbn = childRow.getStatusBarNotification();
+                    if (childSbn.getUser().equals(userHandle) &&
+                            childSbn.getPackageName().equals(pkg)) {
+                        channels.add(childChannel);
+                    }
+                }
+            }
+            try {
+                info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
+                        row.getEntry().channel.getImportance(), sbn, onSettingsClick,
+                        onAppSettingsClick, onDoneClick, mCheckSaveListener,
+                        mNonBlockablePkgs);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        }
+    }
+
+    /**
+     * Closes guts or notification menus that might be visible and saves any changes.
+     *
+     * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
+     * @param force true if guts should be closed regardless of state (used for snooze only).
+     * @param removeControls true if controls (e.g. info) should be closed.
+     * @param x if closed based on touch location, this is the x touch location.
+     * @param y if closed based on touch location, this is the y touch location.
+     * @param resetMenu if any notification menus that might be revealed should be closed.
+     */
+    public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
+            int x, int y, boolean resetMenu) {
+        if (mNotificationGutsExposed != null) {
+            mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
+        }
+        if (resetMenu) {
+            mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
+        }
+    }
+
+    /**
+     * Returns the exposed NotificationGuts or null if none are exposed.
+     */
+    public NotificationGuts getExposedGuts() {
+        return mNotificationGutsExposed;
+    }
+
+    public void setExposedGuts(NotificationGuts guts) {
+        mNotificationGutsExposed = guts;
+    }
+
+    /**
+     *  Opens guts on the given ExpandableNotificationRow |v|.
+     *
+     * @param v ExpandableNotificationRow to open guts on
+     * @param x x coordinate of origin of circular reveal
+     * @param y y coordinate of origin of circular reveal
+     * @param item MenuItem the guts should display
+     * @return true if guts was opened
+     */
+    public boolean openGuts(View v, int x, int y,
+            NotificationMenuRowPlugin.MenuItem item) {
+        if (!(v instanceof ExpandableNotificationRow)) {
+            return false;
+        }
+
+        if (v.getWindowToken() == null) {
+            Log.e(TAG, "Trying to show notification guts, but not attached to window");
+            return false;
+        }
+
+        final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+        if (row.isDark()) {
+            return false;
+        }
+        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        if (row.areGutsExposed()) {
+            closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+                    true /* removeControls */, -1 /* x */, -1 /* y */,
+                    true /* resetMenu */);
+            return false;
+        }
+        bindGuts(row, item);
+        NotificationGuts guts = row.getGuts();
+
+        // Assume we are a status_bar_notification_row
+        if (guts == null) {
+            // This view has no guts. Examples are the more card or the dismiss all view
+            return false;
+        }
+
+        mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS);
+
+        // ensure that it's laid but not visible until actually laid out
+        guts.setVisibility(View.INVISIBLE);
+        // Post to ensure the the guts are properly laid out.
+        guts.post(new Runnable() {
+            @Override
+            public void run() {
+                if (row.getWindowToken() == null) {
+                    Log.e(TAG, "Trying to show notification guts, but not attached to "
+                            + "window");
+                    return;
+                }
+                closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+                        true /* removeControls */, -1 /* x */, -1 /* y */,
+                        false /* resetMenu */);
+                guts.setVisibility(View.VISIBLE);
+                final double horz = Math.max(guts.getWidth() - x, x);
+                final double vert = Math.max(guts.getHeight() - y, y);
+                final float r = (float) Math.hypot(horz, vert);
+                final Animator a
+                        = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
+                a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+                a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+                a.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        super.onAnimationEnd(animation);
+                        // Move the notification view back over the menu
+                        row.resetTranslation();
+                    }
+                });
+                a.start();
+                final boolean needsFalsingProtection =
+                        (mPresenter.isPresenterLocked() &&
+                                !mAccessibilityManager.isTouchExplorationEnabled());
+                guts.setExposed(true /* exposed */, needsFalsingProtection);
+                row.closeRemoteInput();
+                mStackScroller.onHeightChanged(row, true /* needsAnimation */);
+                mNotificationGutsExposed = guts;
+                mGutsMenuItem = item;
+            }
+        });
+        return true;
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("mKeyToRemoveOnGutsClosed: ");
+        pw.println(mKeyToRemoveOnGutsClosed);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 3b23a0c..8d1bb5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -84,7 +84,7 @@
     public interface CheckSaveListener {
         // Invoked when importance has changed and the NotificationInfo wants to try to save it.
         // Listener should run saveImportance unless the change should be canceled.
-        void checkSave(Runnable saveImportance);
+        void checkSave(Runnable saveImportance, StatusBarNotification sbn);
     }
 
     public interface OnSettingsClickListener {
@@ -409,7 +409,7 @@
     public boolean handleCloseControls(boolean save, boolean force) {
         if (save && hasImportanceChanged()) {
             if (mCheckSaveListener != null) {
-                mCheckSaveListener.checkSave(() -> { saveImportance(); });
+                mCheckSaveListener.checkSave(this::saveImportance, mSbn);
             } else {
                 saveImportance();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
new file mode 100644
index 0000000..e65bab2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -0,0 +1,245 @@
+package com.android.systemui.statusbar;
+
+import android.app.Notification;
+import android.content.Context;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.systemui.Dumpable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles tasks and state related to media notifications. For example, there is a 'current' media
+ * notification, which this class keeps track of.
+ */
+public class NotificationMediaManager implements Dumpable {
+    private static final String TAG = "NotificationMediaManager";
+    public static final boolean DEBUG_MEDIA = false;
+
+    private final NotificationPresenter mPresenter;
+    private final Context mContext;
+    private final MediaSessionManager mMediaSessionManager;
+    private MediaController mMediaController;
+    private String mMediaNotificationKey;
+    private MediaMetadata mMediaMetadata;
+
+    private final MediaController.Callback mMediaListener = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            super.onPlaybackStateChanged(state);
+            if (DEBUG_MEDIA) {
+                Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
+            }
+            if (state != null) {
+                if (!isPlaybackActive(state.getState())) {
+                    clearCurrentMediaNotification();
+                    mPresenter.updateMediaMetaData(true, true);
+                }
+            }
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            super.onMetadataChanged(metadata);
+            if (DEBUG_MEDIA) {
+                Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
+            }
+            mMediaMetadata = metadata;
+            mPresenter.updateMediaMetaData(true, true);
+        }
+    };
+
+    public NotificationMediaManager(NotificationPresenter presenter, Context context) {
+        mPresenter = presenter;
+        mContext = context;
+        mMediaSessionManager
+                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
+        // in session state
+    }
+
+    public void onNotificationRemoved(String key) {
+        if (key.equals(mMediaNotificationKey)) {
+            clearCurrentMediaNotification();
+            mPresenter.updateMediaMetaData(true, true);
+        }
+    }
+
+    public String getMediaNotificationKey() {
+        return mMediaNotificationKey;
+    }
+
+    public MediaMetadata getMediaMetadata() {
+        return mMediaMetadata;
+    }
+
+    public void findAndUpdateMediaNotifications() {
+        boolean metaDataChanged = false;
+
+        synchronized (mPresenter.getNotificationData()) {
+            ArrayList<NotificationData.Entry> activeNotifications = mPresenter
+                    .getNotificationData().getActiveNotifications();
+            final int N = activeNotifications.size();
+
+            // Promote the media notification with a controller in 'playing' state, if any.
+            NotificationData.Entry mediaNotification = null;
+            MediaController controller = null;
+            for (int i = 0; i < N; i++) {
+                final NotificationData.Entry entry = activeNotifications.get(i);
+
+                if (isMediaNotification(entry)) {
+                    final MediaSession.Token token =
+                            entry.notification.getNotification().extras.getParcelable(
+                                    Notification.EXTRA_MEDIA_SESSION);
+                    if (token != null) {
+                        MediaController aController = new MediaController(mContext, token);
+                        if (PlaybackState.STATE_PLAYING ==
+                                getMediaControllerPlaybackState(aController)) {
+                            if (DEBUG_MEDIA) {
+                                Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
+                                        + entry.notification.getKey());
+                            }
+                            mediaNotification = entry;
+                            controller = aController;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (mediaNotification == null) {
+                // Still nothing? OK, let's just look for live media sessions and see if they match
+                // one of our notifications. This will catch apps that aren't (yet!) using media
+                // notifications.
+
+                if (mMediaSessionManager != null) {
+                    // TODO: Should this really be for all users?
+                    final List<MediaController> sessions
+                            = mMediaSessionManager.getActiveSessionsForUser(
+                            null,
+                            UserHandle.USER_ALL);
+
+                    for (MediaController aController : sessions) {
+                        if (PlaybackState.STATE_PLAYING ==
+                                getMediaControllerPlaybackState(aController)) {
+                            // now to see if we have one like this
+                            final String pkg = aController.getPackageName();
+
+                            for (int i = 0; i < N; i++) {
+                                final NotificationData.Entry entry = activeNotifications.get(i);
+                                if (entry.notification.getPackageName().equals(pkg)) {
+                                    if (DEBUG_MEDIA) {
+                                        Log.v(TAG, "DEBUG_MEDIA: found controller matching "
+                                                + entry.notification.getKey());
+                                    }
+                                    controller = aController;
+                                    mediaNotification = entry;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (controller != null && !sameSessions(mMediaController, controller)) {
+                // We have a new media session
+                clearCurrentMediaNotification();
+                mMediaController = controller;
+                mMediaController.registerCallback(mMediaListener);
+                mMediaMetadata = mMediaController.getMetadata();
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
+                            + mMediaMetadata);
+                }
+
+                if (mediaNotification != null) {
+                    mMediaNotificationKey = mediaNotification.notification.getKey();
+                    if (DEBUG_MEDIA) {
+                        Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
+                                + mMediaNotificationKey + " controller=" + mMediaController);
+                    }
+                }
+                metaDataChanged = true;
+            }
+        }
+
+        if (metaDataChanged) {
+            mPresenter.updateNotifications();
+        }
+        mPresenter.updateMediaMetaData(metaDataChanged, true);
+    }
+
+    public void clearCurrentMediaNotification() {
+        mMediaNotificationKey = null;
+        mMediaMetadata = null;
+        if (mMediaController != null) {
+            if (DEBUG_MEDIA) {
+                Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
+                        + mMediaController.getPackageName());
+            }
+            mMediaController.unregisterCallback(mMediaListener);
+        }
+        mMediaController = null;
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("    mMediaSessionManager=");
+        pw.println(mMediaSessionManager);
+        pw.print("    mMediaNotificationKey=");
+        pw.println(mMediaNotificationKey);
+        pw.print("    mMediaController=");
+        pw.print(mMediaController);
+        if (mMediaController != null) {
+            pw.print(" state=" + mMediaController.getPlaybackState());
+        }
+        pw.println();
+        pw.print("    mMediaMetadata=");
+        pw.print(mMediaMetadata);
+        if (mMediaMetadata != null) {
+            pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
+        }
+        pw.println();
+    }
+
+    private boolean isPlaybackActive(int state) {
+        return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
+                && state != PlaybackState.STATE_NONE;
+    }
+
+    private boolean sameSessions(MediaController a, MediaController b) {
+        if (a == b) {
+            return true;
+        }
+        if (a == null) {
+            return false;
+        }
+        return a.controlsSameSession(b);
+    }
+
+    private int getMediaControllerPlaybackState(MediaController controller) {
+        if (controller != null) {
+            final PlaybackState playbackState = controller.getPlaybackState();
+            if (playbackState != null) {
+                return playbackState.getState();
+            }
+        }
+        return PlaybackState.STATE_NONE;
+    }
+
+    private boolean isMediaNotification(NotificationData.Entry entry) {
+        // TODO: confirm that there's a valid media key
+        return entry.getExpandedContentView() != null &&
+                entry.getExpandedContentView()
+                        .findViewById(com.android.internal.R.id.media_actions) != null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
new file mode 100644
index 0000000..1aca60c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.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.systemui.statusbar;
+
+import android.content.Intent;
+import android.service.notification.NotificationListenerService;
+
+/**
+ * An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
+ * for both querying the state of the system (some modularised piece of functionality may
+ * want to act differently based on e.g. whether the presenter is visible to the user or not) and
+ * for affecting the state of the system (e.g. starting an intent, given that the presenter may
+ * want to perform some action before doing so).
+ */
+public interface NotificationPresenter {
+
+    /**
+     * Returns true if the presenter is not visible. For example, it may not be necessary to do
+     * animations if this returns true.
+     */
+    boolean isPresenterFullyCollapsed();
+
+    /**
+     * Returns true if the presenter is locked. For example, if the keyguard is active.
+     */
+    boolean isPresenterLocked();
+
+    /**
+     * Returns the current user id. This can change if the user is switched.
+     */
+    int getCurrentUserId();
+
+    /**
+     * Runs the given intent. The presenter may want to run some animations or close itself when
+     * this happens.
+     */
+    void startNotificationGutsIntent(Intent intent, int appUid);
+
+    /**
+     * Returns NotificationData.
+     */
+    NotificationData getNotificationData();
+
+    // TODO: Create NotificationEntryManager and move this method to there.
+    /**
+     * Signals that some notifications have changed, and NotificationPresenter should update itself.
+     */
+    void updateNotifications();
+
+    /**
+     * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
+     */
+    void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
+
+    // TODO: Create NotificationUpdateHandler and move this method to there.
+    /**
+     * Removes a notification.
+     */
+    void removeNotification(String key, NotificationListenerService.RankingMap ranking);
+
+    // TODO: Create NotificationEntryManager and move this method to there.
+    /**
+     * Gets the latest ranking map.
+     */
+    NotificationListenerService.RankingMap getLatestRankingMap();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 4c2a2f5..fc420eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -383,6 +383,12 @@
         }
     }
 
+    @Override
+    public void prepareFadeIn() {
+        super.prepareFadeIn();
+        setVisible(true /* visible */, false /* force */);
+    }
+
     private void resetTransformedView(View child) {
         TransformState ownState = TransformState.createFrom(child, mTransformInfo);
         ownState.resetTransformedView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 1bd90fa..149ec0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -20,7 +20,7 @@
 import android.provider.Settings.Secure;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
@@ -78,8 +78,8 @@
         }
 
         if (!mAutoTracker.isAdded(NIGHT)
-                && NightDisplayController.isAvailable(mContext)) {
-            Dependency.get(NightDisplayController.class).setListener(mNightDisplayCallback);
+                && ColorDisplayController.isAvailable(mContext)) {
+            Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
         }
     }
 
@@ -89,7 +89,7 @@
         Dependency.get(HotspotController.class).removeCallback(mHotspotCallback);
         Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
         Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
-        Dependency.get(NightDisplayController.class).setListener(null);
+        Dependency.get(ColorDisplayController.class).setListener(null);
     }
 
     private final ManagedProfileController.Callback mProfileCallback =
@@ -139,8 +139,8 @@
     };
 
     @VisibleForTesting
-    final NightDisplayController.Callback mNightDisplayCallback =
-            new NightDisplayController.Callback() {
+    final ColorDisplayController.Callback mColorDisplayCallback =
+            new ColorDisplayController.Callback() {
         @Override
         public void onActivated(boolean activated) {
             if (activated) {
@@ -150,8 +150,8 @@
 
         @Override
         public void onAutoModeChanged(int autoMode) {
-            if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM
-                    || autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
+            if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM
+                    || autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
                 addNightTile();
             }
         }
@@ -160,7 +160,7 @@
             if (mAutoTracker.isAdded(NIGHT)) return;
             mHost.addTile(NIGHT);
             mAutoTracker.setTileAdded(NIGHT);
-            mHandler.post(() -> Dependency.get(NightDisplayController.class)
+            mHandler.post(() -> Dependency.get(ColorDisplayController.class)
                     .setListener(null));
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 3ba9ac6..4180884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -35,7 +35,7 @@
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-import com.android.systemui.shared.recents.model.IOverviewProxy;
+import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.tuner.TunerService;
 
@@ -112,20 +112,26 @@
         mIsRTL = isRTL;
     }
 
-    public boolean onInterceptTouchEvent(MotionEvent event) {
+    private boolean proxyMotionEvents(MotionEvent event) {
         final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
         if (overviewProxy != null) {
             mNavigationBarView.requestUnbufferedDispatch(event);
             try {
                 overviewProxy.onMotionEvent(event);
+                return true;
             } catch (RemoteException e) {
                 Log.e(TAG, "Callback failed", e);
             }
         }
+        return false;
+    }
 
-        // If we move more than a fixed amount, then start capturing for the
-        // task switcher detector
-        mTaskSwitcherDetector.onTouchEvent(event);
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (!proxyMotionEvents(event)) {
+            // If we move more than a fixed amount, then start capturing for the
+            // task switcher detector, disabled when proxying motion events to launcher service
+            mTaskSwitcherDetector.onTouchEvent(event);
+        }
         int action = event.getAction();
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
@@ -289,7 +295,7 @@
     }
 
     public boolean onTouchEvent(MotionEvent event) {
-        boolean result = mTaskSwitcherDetector.onTouchEvent(event);
+        boolean result = proxyMotionEvents(event) || mTaskSwitcherDetector.onTouchEvent(event);
         if (mDockWindowEnabled) {
             result |= handleDockWindowEvent(event);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 094129c..4e7f205 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -47,6 +47,8 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.DockedStackExistsListener;
+import com.android.systemui.OverviewProxyService;
+import com.android.systemui.OverviewProxyService.OverviewProxyListener;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.plugins.PluginListener;
@@ -196,6 +198,9 @@
         }
     }
 
+    private final OverviewProxyListener mOverviewProxyListener =
+            isConnected -> setSlippery(!isConnected);
+
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -531,6 +536,24 @@
         }
     }
 
+    private void setSlippery(boolean slippery) {
+        boolean changed = false;
+        final ViewGroup navbarView = ((ViewGroup) getParent());
+        final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView
+                .getLayoutParams();
+        if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) {
+            lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
+            changed = true;
+        } else if (!slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0) {
+            lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
+            changed = true;
+        }
+        if (changed) {
+            WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
+            wm.updateViewLayout(navbarView, lp);
+        }
+    }
+
     public void setMenuVisibility(final boolean show) {
         setMenuVisibility(show, false);
     }
@@ -756,6 +779,7 @@
         onPluginDisconnected(null); // Create default gesture helper
         Dependency.get(PluginManager.class).addPluginListener(this,
                 NavGesture.class, false /* Only one */);
+        Dependency.get(OverviewProxyService.class).addCallback(mOverviewProxyListener);
     }
 
     @Override
@@ -765,6 +789,7 @@
         if (mGestureHelper != null) {
             mGestureHelper.destroy();
         }
+        Dependency.get(OverviewProxyService.class).removeCallback(mOverviewProxyListener);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index af03440..86a8f41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -596,7 +596,7 @@
             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
         }
         closeQs();
-        mStatusBar.closeAndSaveGuts(true /* leavebehind */, true /* force */,
+        mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
                 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
         mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
                 true /* cancelAnimators */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 46e3aa1..e3ed581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -20,11 +20,12 @@
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
-
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
 import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -40,10 +41,8 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.INotificationManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
-import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -80,10 +79,7 @@
 import android.graphics.drawable.Drawable;
 import android.media.AudioAttributes;
 import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -115,14 +111,12 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.view.Display;
-import android.view.HapticFeedbackConstants;
 import android.view.IWindowManager;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.ThreadedRenderer;
 import android.view.View;
-import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.ViewTreeObserver;
@@ -181,7 +175,6 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanel;
@@ -207,10 +200,11 @@
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationGuts;
+import com.android.systemui.statusbar.NotificationGutsManager;
 import com.android.systemui.statusbar.NotificationInfo;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.NotificationSnooze;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.SignalClusterView;
@@ -243,7 +237,6 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout
         .OnChildLocationsChangedListener;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
 import com.android.systemui.util.NotificationChannels;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.volume.VolumeComponent;
@@ -255,10 +248,8 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.Stack;
 
 public class StatusBar extends SystemUI implements DemoMode,
@@ -267,7 +258,7 @@
         ActivatableNotificationView.OnActivatedListener,
         ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
         ExpandableNotificationRow.OnExpandClickListener, InflationCallback,
-        ColorExtractor.OnColorsChangedListener, ConfigurationListener {
+        ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
     public static final boolean MULTIUSER_DEBUG = false;
 
     public static final boolean ENABLE_REMOTE_INPUT =
@@ -287,9 +278,6 @@
     protected static final boolean ENABLE_HEADS_UP = true;
     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
-    // Must match constant in Settings. Used to highlight preferences when linking to Settings.
-    private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
-
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     // Should match the values in PhoneWindowManager
@@ -308,7 +296,6 @@
     public static final boolean SPEW = false;
     public static final boolean DUMPTRUCK = true; // extra dumpsys info
     public static final boolean DEBUG_GESTURES = false;
-    public static final boolean DEBUG_MEDIA = false;
     public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
     public static final boolean DEBUG_CAMERA_LIFT = false;
 
@@ -458,6 +445,8 @@
     private final int[] mAbsPos = new int[2];
     private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
 
+    private NotificationGutsManager mGutsManager;
+
     // for disabling the status bar
     private int mDisabled1 = 0;
     private int mDisabled2 = 0;
@@ -553,31 +542,7 @@
     protected final PorterDuffXfermode mSrcOverXferMode =
             new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
 
-    private MediaSessionManager mMediaSessionManager;
-    private MediaController mMediaController;
-    private String mMediaNotificationKey;
-    private MediaMetadata mMediaMetadata;
-    private final MediaController.Callback mMediaListener = new MediaController.Callback() {
-        @Override
-        public void onPlaybackStateChanged(PlaybackState state) {
-            super.onPlaybackStateChanged(state);
-            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
-            if (state != null) {
-                if (!isPlaybackActive(state.getState())) {
-                    clearCurrentMediaNotification();
-                    updateMediaMetaData(true, true);
-                }
-            }
-        }
-
-        @Override
-        public void onMetadataChanged(MediaMetadata metadata) {
-            super.onMetadataChanged(metadata);
-            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
-            mMediaMetadata = metadata;
-            updateMediaMetaData(true, true);
-        }
-    };
+    private NotificationMediaManager mMediaManager;
 
     /** Keys of notifications currently visible to the user. */
     private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
@@ -700,7 +665,6 @@
     private final HashMap<String, Entry> mPendingNotifications = new HashMap<>();
     private boolean mClearAllEnabled;
     @Nullable private View mAmbientIndicationContainer;
-    private String mKeyToRemoveOnGutsClosed;
     private SysuiColorExtractor mColorExtractor;
     private ForegroundServiceController mForegroundServiceController;
     private ScreenLifecycle mScreenLifecycle;
@@ -817,6 +781,8 @@
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
         mLockPatternUtils = new LockPatternUtils(mContext);
 
+        mMediaManager = new NotificationMediaManager(this, mContext);
+
         // Connect in to the status bar manager service
         mCommandQueue = getComponent(CommandQueue.class);
         mCommandQueue.addCallbacks(this);
@@ -903,16 +869,8 @@
             Slog.e(TAG, "Failed to register VR mode state listener: " + e);
         }
 
-        mNonBlockablePkgs = new HashSet<>();
-        Collections.addAll(mNonBlockablePkgs, res.getStringArray(
-                com.android.internal.R.array.config_nonBlockableNotificationPackages));
         // end old BaseStatusBar.start().
 
-        mMediaSessionManager
-                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
-        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
-        // in session state
-
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
         mSettingsObserver.onChange(false); // set up
@@ -960,6 +918,8 @@
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
         mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
+        mGutsManager = new NotificationGutsManager(this, mStackScroller,
+                mCheckSaveListener, mContext);
         mNotificationPanel.setStatusBar(this);
         mNotificationPanel.setGroupManager(mGroupManager);
         mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
@@ -1302,12 +1262,12 @@
         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
         for (int i = 0; i < activeNotifications.size(); i++) {
             Entry entry = activeNotifications.get(i);
-            boolean exposedGuts = mNotificationGutsExposed != null
-                    && entry.row.getGuts() == mNotificationGutsExposed;
+            boolean exposedGuts = mGutsManager.getExposedGuts() != null
+                    && entry.row.getGuts() == mGutsManager.getExposedGuts();
             entry.row.onDensityOrFontScaleChanged();
             if (exposedGuts) {
-                mNotificationGutsExposed = entry.row.getGuts();
-                bindGuts(entry.row, mGutsMenuItem);
+                mGutsManager.setExposedGuts(entry.row.getGuts());
+                mGutsManager.bindGuts(entry.row);
             }
         }
     }
@@ -1672,6 +1632,7 @@
         updateNotifications();
     }
 
+    @Override
     public void removeNotification(String key, RankingMap ranking) {
         boolean deferRemoval = false;
         abortExistingInflation(key);
@@ -1685,10 +1646,8 @@
                     || !mVisualStabilityManager.isReorderingAllowed();
             deferRemoval = !mHeadsUpManager.removeNotification(key,  ignoreEarliestRemovalTime);
         }
-        if (key.equals(mMediaNotificationKey)) {
-            clearCurrentMediaNotification();
-            updateMediaMetaData(true, true);
-        }
+        mMediaManager.onNotificationRemoved(key);
+
         Entry entry = mNotificationData.get(key);
         if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)
                 && entry.row != null && !entry.row.isDismissed()) {
@@ -1744,12 +1703,12 @@
             mRemoteInputEntriesToRemoveOnCollapse.add(entry);
             return;
         }
-        if (entry != null && mNotificationGutsExposed != null
-                && mNotificationGutsExposed == entry.row.getGuts() && entry.row.getGuts() != null
-                && !entry.row.getGuts().isLeavebehind()) {
+        if (entry != null && mGutsManager.getExposedGuts() != null
+                && mGutsManager.getExposedGuts() == entry.row.getGuts()
+                && entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
             Log.w(TAG, "Keeping notification because it's showing guts. " + key);
             mLatestRankingMap = ranking;
-            mKeyToRemoveOnGutsClosed = key;
+            mGutsManager.setKeyToRemoveOnGutsClosed(key);
             return;
         }
 
@@ -2142,7 +2101,8 @@
         return entry.row.getParent() instanceof NotificationStackScrollLayout;
     }
 
-    protected void updateNotifications() {
+    @Override
+    public void updateNotifications() {
         mNotificationData.filterAndSort();
 
         updateNotificationShade();
@@ -2184,136 +2144,9 @@
             }
         }
 
-        findAndUpdateMediaNotifications();
+        mMediaManager.findAndUpdateMediaNotifications();
     }
 
-    public void findAndUpdateMediaNotifications() {
-        boolean metaDataChanged = false;
-
-        synchronized (mNotificationData) {
-            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
-            final int N = activeNotifications.size();
-
-            // Promote the media notification with a controller in 'playing' state, if any.
-            Entry mediaNotification = null;
-            MediaController controller = null;
-            for (int i = 0; i < N; i++) {
-                final Entry entry = activeNotifications.get(i);
-
-                if (isMediaNotification(entry)) {
-                    final MediaSession.Token token =
-                            entry.notification.getNotification().extras.getParcelable(
-                                    Notification.EXTRA_MEDIA_SESSION);
-                    if (token != null) {
-                        MediaController aController = new MediaController(mContext, token);
-                        if (PlaybackState.STATE_PLAYING ==
-                                getMediaControllerPlaybackState(aController)) {
-                            if (DEBUG_MEDIA) {
-                                Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
-                                        + entry.notification.getKey());
-                            }
-                            mediaNotification = entry;
-                            controller = aController;
-                            break;
-                        }
-                    }
-                }
-            }
-            if (mediaNotification == null) {
-                // Still nothing? OK, let's just look for live media sessions and see if they match
-                // one of our notifications. This will catch apps that aren't (yet!) using media
-                // notifications.
-
-                if (mMediaSessionManager != null) {
-                    final List<MediaController> sessions
-                            = mMediaSessionManager.getActiveSessionsForUser(
-                                    null,
-                                    UserHandle.USER_ALL);
-
-                    for (MediaController aController : sessions) {
-                        if (PlaybackState.STATE_PLAYING ==
-                                getMediaControllerPlaybackState(aController)) {
-                            // now to see if we have one like this
-                            final String pkg = aController.getPackageName();
-
-                            for (int i = 0; i < N; i++) {
-                                final Entry entry = activeNotifications.get(i);
-                                if (entry.notification.getPackageName().equals(pkg)) {
-                                    if (DEBUG_MEDIA) {
-                                        Log.v(TAG, "DEBUG_MEDIA: found controller matching "
-                                                + entry.notification.getKey());
-                                    }
-                                    controller = aController;
-                                    mediaNotification = entry;
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (controller != null && !sameSessions(mMediaController, controller)) {
-                // We have a new media session
-                clearCurrentMediaNotification();
-                mMediaController = controller;
-                mMediaController.registerCallback(mMediaListener);
-                mMediaMetadata = mMediaController.getMetadata();
-                if (DEBUG_MEDIA) {
-                    Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
-                            + mMediaMetadata);
-                }
-
-                if (mediaNotification != null) {
-                    mMediaNotificationKey = mediaNotification.notification.getKey();
-                    if (DEBUG_MEDIA) {
-                        Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
-                                + mMediaNotificationKey + " controller=" + mMediaController);
-                    }
-                }
-                metaDataChanged = true;
-            }
-        }
-
-        if (metaDataChanged) {
-            updateNotifications();
-        }
-        updateMediaMetaData(metaDataChanged, true);
-    }
-
-    private int getMediaControllerPlaybackState(MediaController controller) {
-        if (controller != null) {
-            final PlaybackState playbackState = controller.getPlaybackState();
-            if (playbackState != null) {
-                return playbackState.getState();
-            }
-        }
-        return PlaybackState.STATE_NONE;
-    }
-
-    private boolean isPlaybackActive(int state) {
-        return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
-                && state != PlaybackState.STATE_NONE;
-    }
-
-    private void clearCurrentMediaNotification() {
-        mMediaNotificationKey = null;
-        mMediaMetadata = null;
-        if (mMediaController != null) {
-            if (DEBUG_MEDIA) {
-                Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
-                        + mMediaController.getPackageName());
-            }
-            mMediaController.unregisterCallback(mMediaListener);
-        }
-        mMediaController = null;
-    }
-
-    private boolean sameSessions(MediaController a, MediaController b) {
-        if (a == b) return true;
-        if (a == null) return false;
-        return a.controlsSameSession(b);
-    }
 
     /**
      * Hide the album artwork that is fading out and release its bitmap.
@@ -2330,9 +2163,11 @@
         }
     };
 
+    // TODO: Move this to NotificationMediaManager.
     /**
      * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
      */
+    @Override
     public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
         Trace.beginSection("StatusBar#updateMediaMetaData");
         if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
@@ -2351,19 +2186,22 @@
             return;
         }
 
+        MediaMetadata mediaMetadata = mMediaManager.getMediaMetadata();
+
         if (DEBUG_MEDIA) {
-            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
-                    + " metadata=" + mMediaMetadata
+            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification "
+                    + mMediaManager.getMediaNotificationKey()
+                    + " metadata=" + mediaMetadata
                     + " metaDataChanged=" + metaDataChanged
                     + " state=" + mState);
         }
 
         Drawable artworkDrawable = null;
-        if (mMediaMetadata != null) {
+        if (mediaMetadata != null) {
             Bitmap artworkBitmap = null;
-            artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+            artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
             if (artworkBitmap == null) {
-                artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+                artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
                 // might still be null
             }
             if (artworkBitmap != null) {
@@ -2636,7 +2474,7 @@
 
     @Override  // NotificationData.Environment
     public String getCurrentMediaNotificationKey() {
-        return mMediaNotificationKey;
+        return mMediaManager.getMediaNotificationKey();
     }
 
     public boolean isScrimSrcModeEnabled() {
@@ -3101,8 +2939,8 @@
         mStatusBarWindowManager.setForceStatusBarVisible(false);
 
         // Close any guts that might be visible
-        closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */,
-                -1 /* x */, -1 /* y */, true /* resetMenu */);
+        mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+                true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
 
         runPostCollapseRunnables();
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
@@ -3449,28 +3287,18 @@
         pw.println(Settings.Global.zenModeToString(mZenMode));
         pw.print("  mUseHeadsUp=");
         pw.println(mUseHeadsUp);
-        pw.print("  mKeyToRemoveOnGutsClosed=");
-        pw.println(mKeyToRemoveOnGutsClosed);
+        pw.print("  mGutsManager: ");
+        if (mGutsManager != null) {
+            mGutsManager.dump(fd, pw, args);
+        }
         if (mStatusBarView != null) {
             dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
         }
 
-        pw.print("  mMediaSessionManager=");
-        pw.println(mMediaSessionManager);
-        pw.print("  mMediaNotificationKey=");
-        pw.println(mMediaNotificationKey);
-        pw.print("  mMediaController=");
-        pw.print(mMediaController);
-        if (mMediaController != null) {
-            pw.print(" state=" + mMediaController.getPlaybackState());
+        pw.println("  mMediaManager: ");
+        if (mMediaManager != null) {
+            mMediaManager.dump(fd, pw, args);
         }
-        pw.println();
-        pw.print("  mMediaMetadata=");
-        pw.print(mMediaMetadata);
-        if (mMediaMetadata != null) {
-            pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
-        }
-        pw.println();
 
         pw.println("  Panels: ");
         if (mNotificationPanel != null) {
@@ -3792,7 +3620,7 @@
             mReinflateNotificationsOnUserSwitched = false;
         }
         updateNotificationShade();
-        clearCurrentMediaNotification();
+        mMediaManager.clearCurrentMediaNotification();
         setLockscreenUser(newUserId);
     }
 
@@ -3872,10 +3700,10 @@
             if (visibleToUser) {
                 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
                 boolean clearNotificationEffects =
-                        !isPanelFullyCollapsed() &&
+                        !isPresenterFullyCollapsed() &&
                         (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
                 int notificationLoad = mNotificationData.getActiveNotifications().size();
-                if (pinnedHeadsUp && isPanelFullyCollapsed())  {
+                if (pinnedHeadsUp && isPresenterFullyCollapsed())  {
                     notificationLoad = 1;
                 }
                 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
@@ -4152,7 +3980,8 @@
         return mState;
     }
 
-    public boolean isPanelFullyCollapsed() {
+    @Override
+    public boolean isPresenterFullyCollapsed() {
         return mNotificationPanel.isFullyCollapsed();
     }
 
@@ -4735,7 +4564,7 @@
 
     public void onClosingFinished() {
         runPostCollapseRunnables();
-        if (!isPanelFullyCollapsed()) {
+        if (!isPresenterFullyCollapsed()) {
             // if we set it not to be focusable when collapsing, we have to undo it when we aborted
             // the closing
             mStatusBarWindowManager.setStatusBarFocusable(true);
@@ -5600,10 +5429,6 @@
 
     protected int mZenMode;
 
-    // which notification is currently being longpress-examined by the user
-    private NotificationGuts mNotificationGutsExposed;
-    private MenuItem mGutsMenuItem;
-
     protected NotificationShelf mNotificationShelf;
     protected DismissView mDismissView;
     protected EmptyShadeView mEmptyShadeView;
@@ -5614,8 +5439,6 @@
 
     protected boolean mVrMode;
 
-    private Set<String> mNonBlockablePkgs;
-
     public boolean isDeviceInteractive() {
         return mDeviceInteractive;
     }
@@ -6147,26 +5970,8 @@
         return mGroupManager;
     }
 
-    public boolean isMediaNotification(NotificationData.Entry entry) {
-        // TODO: confirm that there's a valid media key
-        return entry.getExpandedContentView() != null &&
-               entry.getExpandedContentView()
-                       .findViewById(com.android.internal.R.id.media_actions) != null;
-    }
-
-    // The button in the guts that links to the system notification settings for that app
-    private void startAppNotificationSettingsActivity(String packageName, final int appUid,
-            final NotificationChannel channel) {
-        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
-        intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
-        intent.putExtra(Settings.EXTRA_APP_UID, appUid);
-        if (channel != null) {
-            intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
-        }
-        startNotificationGutsIntent(intent, appUid);
-    }
-
-    private void startNotificationGutsIntent(final Intent intent, final int appUid) {
+    @Override
+    public void startNotificationGutsIntent(final Intent intent, final int appUid) {
         dismissKeyguardThenExecute(() -> {
             AsyncTask.execute(() -> {
                 TaskStackBuilder.create(mContext)
@@ -6189,231 +5994,8 @@
         }
     }
 
-    private void bindGuts(final ExpandableNotificationRow row, MenuItem item) {
-        row.inflateGuts();
-        row.setGutsView(item);
-        final StatusBarNotification sbn = row.getStatusBarNotification();
-        row.setTag(sbn.getPackageName());
-        final NotificationGuts guts = row.getGuts();
-        guts.setClosedListener((NotificationGuts g) -> {
-            if (!g.willBeRemoved() && !row.isRemoved()) {
-                mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */);
-            }
-            if (mNotificationGutsExposed == g) {
-                mNotificationGutsExposed = null;
-                mGutsMenuItem = null;
-            }
-            String key = sbn.getKey();
-            if (key.equals(mKeyToRemoveOnGutsClosed)) {
-                mKeyToRemoveOnGutsClosed = null;
-                removeNotification(key, mLatestRankingMap);
-            }
-        });
-
-        View gutsView = item.getGutsView();
-        if (gutsView instanceof NotificationSnooze) {
-            NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
-            snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
-            snoozeGuts.setStatusBarNotification(sbn);
-            snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
-            guts.setHeightChangedListener((NotificationGuts g) -> {
-                mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
-            });
-        }
-
-        if (gutsView instanceof NotificationInfo) {
-            final UserHandle userHandle = sbn.getUser();
-            PackageManager pmUser = getPackageManagerForUser(mContext,
-                    userHandle.getIdentifier());
-            final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
-                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-            final String pkg = sbn.getPackageName();
-            NotificationInfo info = (NotificationInfo) gutsView;
-            // Settings link is only valid for notifications that specify a user, unless this is the
-            // system user.
-            NotificationInfo.OnSettingsClickListener onSettingsClick = null;
-            if (!userHandle.equals(UserHandle.ALL) || mCurrentUserId == UserHandle.USER_SYSTEM) {
-                onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
-                    mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO);
-                    guts.resetFalsingCheck();
-                    startAppNotificationSettingsActivity(pkg, appUid, channel);
-                };
-            }
-            final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
-                    Intent intent) -> {
-                mMetricsLogger.action(MetricsEvent.ACTION_APP_NOTE_SETTINGS);
-                guts.resetFalsingCheck();
-                startNotificationGutsIntent(intent, sbn.getUid());
-            };
-            final View.OnClickListener onDoneClick = (View v) -> {
-                saveAndCloseNotificationMenu(row, guts, v);
-            };
-            final NotificationInfo.CheckSaveListener checkSaveListener =
-                    (Runnable saveImportance) -> {
-                // If the user has security enabled, show challenge if the setting is changed.
-                if (isLockscreenPublicMode(userHandle.getIdentifier())
-                        && (mState == StatusBarState.KEYGUARD
-                                || mState == StatusBarState.SHADE_LOCKED)) {
-                    onLockedNotificationImportanceChange(() -> {
-                        saveImportance.run();
-                        return true;
-                    });
-                } else {
-                    saveImportance.run();
-                }
-            };
-
-            ArraySet<NotificationChannel> channels = new ArraySet<>();
-            channels.add(row.getEntry().channel);
-            if (row.isSummaryWithChildren()) {
-                // If this is a summary, then add in the children notification channels for the
-                // same user and pkg.
-                final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
-                final int numChildren = childrenRows.size();
-                for (int i = 0; i < numChildren; i++) {
-                    final ExpandableNotificationRow childRow = childrenRows.get(i);
-                    final NotificationChannel childChannel = childRow.getEntry().channel;
-                    final StatusBarNotification childSbn = childRow.getStatusBarNotification();
-                    if (childSbn.getUser().equals(userHandle) &&
-                            childSbn.getPackageName().equals(pkg)) {
-                        channels.add(childChannel);
-                    }
-                }
-            }
-            try {
-                info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
-                        row.getEntry().channel.getImportance(), sbn, onSettingsClick,
-                        onAppSettingsClick, onDoneClick, checkSaveListener,
-                        mNonBlockablePkgs);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        }
-    }
-
-    private void saveAndCloseNotificationMenu(
-            ExpandableNotificationRow row, NotificationGuts guts, View done) {
-        guts.resetFalsingCheck();
-        int[] rowLocation = new int[2];
-        int[] doneLocation = new int[2];
-        row.getLocationOnScreen(rowLocation);
-        done.getLocationOnScreen(doneLocation);
-
-        final int centerX = done.getWidth() / 2;
-        final int centerY = done.getHeight() / 2;
-        final int x = doneLocation[0] - rowLocation[0] + centerX;
-        final int y = doneLocation[1] - rowLocation[1] + centerY;
-        closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
-                true /* removeControls */, x, y, true /* resetMenu */);
-    }
-
     protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
-        return new ExpandableNotificationRow.LongPressListener() {
-            @Override
-            public boolean onLongPress(View v, final int x, final int y,
-                    MenuItem item) {
-                if (!(v instanceof ExpandableNotificationRow)) {
-                    return false;
-                }
-
-                if (v.getWindowToken() == null) {
-                    Log.e(TAG, "Trying to show notification guts, but not attached to window");
-                    return false;
-                }
-
-                final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-                if (row.isDark()) {
-                    return false;
-                }
-                v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                if (row.areGutsExposed()) {
-                    closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
-                            true /* removeControls */, -1 /* x */, -1 /* y */,
-                            true /* resetMenu */);
-                    return true;
-                }
-                bindGuts(row, item);
-                NotificationGuts guts = row.getGuts();
-
-                // Assume we are a status_bar_notification_row
-                if (guts == null) {
-                    // This view has no guts. Examples are the more card or the dismiss all view
-                    return false;
-                }
-
-                mMetricsLogger.action(MetricsEvent.ACTION_NOTE_CONTROLS);
-
-                // ensure that it's laid but not visible until actually laid out
-                guts.setVisibility(View.INVISIBLE);
-                // Post to ensure the the guts are properly laid out.
-                guts.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (row.getWindowToken() == null) {
-                            Log.e(TAG, "Trying to show notification guts, but not attached to "
-                                    + "window");
-                            return;
-                        }
-                        closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
-                                true /* removeControls */, -1 /* x */, -1 /* y */,
-                                false /* resetMenu */);
-                        guts.setVisibility(View.VISIBLE);
-                        final double horz = Math.max(guts.getWidth() - x, x);
-                        final double vert = Math.max(guts.getHeight() - y, y);
-                        final float r = (float) Math.hypot(horz, vert);
-                        final Animator a
-                                = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
-                        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                        a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-                        a.addListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                super.onAnimationEnd(animation);
-                                // Move the notification view back over the menu
-                                row.resetTranslation();
-                            }
-                        });
-                        a.start();
-                        final boolean needsFalsingProtection =
-                                (mState == StatusBarState.KEYGUARD &&
-                                !mAccessibilityManager.isTouchExplorationEnabled());
-                        guts.setExposed(true /* exposed */, needsFalsingProtection);
-                        row.closeRemoteInput();
-                        mStackScroller.onHeightChanged(row, true /* needsAnimation */);
-                        mNotificationGutsExposed = guts;
-                        mGutsMenuItem = item;
-                    }
-                });
-                return true;
-            }
-        };
-    }
-
-    /**
-     * Returns the exposed NotificationGuts or null if none are exposed.
-     */
-    public NotificationGuts getExposedGuts() {
-        return mNotificationGutsExposed;
-    }
-
-    /**
-     * Closes guts or notification menus that might be visible and saves any changes.
-     *
-     * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
-     * @param force true if guts should be closed regardless of state (used for snooze only).
-     * @param removeControls true if controls (e.g. info) should be closed.
-     * @param x if closed based on touch location, this is the x touch location.
-     * @param y if closed based on touch location, this is the y touch location.
-     * @param resetMenu if any notification menus that might be revealed should be closed.
-     */
-    public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
-            int x, int y, boolean resetMenu) {
-        if (mNotificationGutsExposed != null) {
-            mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
-        }
-        if (resetMenu) {
-            mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
-        }
+        return (v, x, y, item) -> mGutsManager.openGuts(v, x, y, item);
     }
 
     @Override
@@ -6802,7 +6384,7 @@
                 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
                     // Release the HUN notification to the shade.
 
-                    if (isPanelFullyCollapsed()) {
+                    if (isPresenterFullyCollapsed()) {
                         HeadsUpManager.setIsClickedNotification(row, true);
                     }
                     //
@@ -6934,7 +6516,7 @@
         if (mVisible != visible) {
             mVisible = visible;
             if (!visible) {
-                closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+                mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
                         true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
             }
         }
@@ -7156,8 +6738,8 @@
         }
         mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
         mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
-        if (key.equals(mKeyToRemoveOnGutsClosed)) {
-            mKeyToRemoveOnGutsClosed = null;
+        if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
+            mGutsManager.setKeyToRemoveOnGutsClosed(null);
             Log.w(TAG, "Notification that was kept for guts was updated. " + key);
         }
 
@@ -7355,4 +6937,43 @@
             mNavigationBar.getBarTransitions().setAutoDim(true);
         }
     };
+
+    public NotificationGutsManager getGutsManager() {
+        return mGutsManager;
+    }
+
+    @Override
+    public boolean isPresenterLocked() {
+        return mState == StatusBarState.KEYGUARD;
+    }
+
+    @Override
+    public int getCurrentUserId() {
+        return mCurrentUserId;
+    }
+
+    @Override
+    public NotificationData getNotificationData() {
+        return mNotificationData;
+    }
+
+    @Override
+    public RankingMap getLatestRankingMap() {
+        return mLatestRankingMap;
+    }
+
+    final NotificationInfo.CheckSaveListener mCheckSaveListener =
+            (Runnable saveImportance, StatusBarNotification sbn) -> {
+                // If the user has security enabled, show challenge if the setting is changed.
+                if (isLockscreenPublicMode(sbn.getUser().getIdentifier()) && (
+                        mState == StatusBarState.KEYGUARD
+                                || mState == StatusBarState.SHADE_LOCKED)) {
+                    onLockedNotificationImportanceChange(() -> {
+                        saveImportance.run();
+                        return true;
+                    });
+                } else {
+                    saveImportance.run();
+                }
+            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 1e14626..efe049a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1343,7 +1343,7 @@
         }
 
         // Check if we need to clear any snooze leavebehinds
-        NotificationGuts guts = mStatusBar.getExposedGuts();
+        NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
         if (guts != null && !isTouchInView(ev, guts)
                 && guts.getGutsContent() instanceof NotificationSnooze) {
             NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
@@ -2508,12 +2508,13 @@
         }
         // Check if we need to clear any snooze leavebehinds
         boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
-        NotificationGuts guts = mStatusBar.getExposedGuts();
+        NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
         if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
                 && !scrollWantsIt) {
             mCheckForLeavebehind = false;
-            mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
-                    false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
         }
         if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
             mCheckForLeavebehind = true;
@@ -3065,7 +3066,7 @@
             }
             if (!childWasSwipedOut) {
                 Rect clipBounds = child.getClipBounds();
-                childWasSwipedOut = clipBounds.height() == 0;
+                childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
             }
             int animationType = childWasSwipedOut
                     ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
@@ -3355,8 +3356,9 @@
 
     public void checkSnoozeLeavebehind() {
         if (mCheckForLeavebehind) {
-            mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
-                    false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
             mCheckForLeavebehind = false;
         }
     }
@@ -4438,8 +4440,9 @@
                 // of the panel early.
                 handleChildDismissed(view);
             }
-            mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
-                    false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+            mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
             handleMenuCoveredOrDismissed();
         }
 
@@ -4524,7 +4527,7 @@
         }
 
         public void closeControlsIfOutsideTouch(MotionEvent ev) {
-            NotificationGuts guts = mStatusBar.getExposedGuts();
+            NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
             View view = null;
             if (guts != null && !guts.getGutsContent().isLeavebehind()) {
                 // Only close visible guts if they're not a leavebehind.
@@ -4536,8 +4539,9 @@
             }
             if (view != null && !isTouchInView(ev, view)) {
                 // Touch was outside visible guts / menu notification, close what's visible
-                mStatusBar.closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
-                        true /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+                mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
+                        false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+                        false /* resetMenu */);
                 resetExposedMenuView(true /* animate */, true /* force */);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index f91e45d..27bf534 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -169,16 +169,23 @@
         protected boolean persistBoolean(boolean value) {
             final int desiredState = value ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                     : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+            boolean shouldSendBroadcast = false;
             for (int i = 0; i < mInfo.services.length; i++) {
                 ComponentName componentName = new ComponentName(mInfo.packageName,
                         mInfo.services[i].name);
-                mPm.setComponentEnabledSetting(componentName, desiredState,
-                        PackageManager.DONT_KILL_APP);
+
+                if (mPm.getComponentEnabledSetting(componentName) != desiredState) {
+                    mPm.setComponentEnabledSetting(componentName, desiredState,
+                            PackageManager.DONT_KILL_APP);
+                    shouldSendBroadcast = true;
+                }
             }
-            final String pkg = mInfo.packageName;
-            final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED,
-                    pkg != null ? Uri.fromParts("package", pkg, null) : null);
-            getContext().sendBroadcast(intent);
+            if (shouldSendBroadcast) {
+                final String pkg = mInfo.packageName;
+                final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED,
+                        pkg != null ? Uri.fromParts("package", pkg, null) : null);
+                getContext().sendBroadcast(intent);
+            }
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
index 3eccccd..e117969 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
@@ -71,10 +71,12 @@
         ap.mIcon = mResolveInfo.loadIcon(packageManager);
         ap.mTitle = appName;
         if (mDevice == null) {
-            ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName);
+            ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName,
+                    mAccessory.getDescription());
             mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
         } else {
-            ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName);
+            ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName,
+                    mDevice.getProductName());
             mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
         }
         ap.mPositiveButtonText = getString(android.R.string.ok);
@@ -88,9 +90,11 @@
         ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
         mAlwaysUse = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
         if (mDevice == null) {
-            mAlwaysUse.setText(R.string.always_use_accessory);
+            mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+                    mAccessory.getDescription()));
         } else {
-            mAlwaysUse.setText(R.string.always_use_device);
+            mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+                    mDevice.getProductName()));
         }
         mAlwaysUse.setOnCheckedChangeListener(this);
         mClearDefaultHint = (TextView)ap.mView.findViewById(
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 1e69fc5..87d11b2 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,13 +16,17 @@
 
 package com.android.systemui.usb;
 
+import android.annotation.NonNull;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.res.XmlResourceParser;
 import android.hardware.usb.IUsbManager;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
@@ -41,8 +45,13 @@
 
 import com.android.internal.app.AlertActivity;
 import com.android.internal.app.AlertController;
+import com.android.internal.util.XmlUtils;
+import android.hardware.usb.AccessoryFilter;
+import android.hardware.usb.DeviceFilter;
 import com.android.systemui.R;
 
+import org.xmlpull.v1.XmlPullParser;
+
 public class UsbPermissionActivity extends AlertActivity
         implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
 
@@ -84,10 +93,12 @@
         ap.mIcon = aInfo.loadIcon(packageManager);
         ap.mTitle = appName;
         if (mDevice == null) {
-            ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName);
+            ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName,
+                    mAccessory.getDescription());
             mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
         } else {
-            ap.mMessage = getString(R.string.usb_device_permission_prompt, appName);
+            ap.mMessage = getString(R.string.usb_device_permission_prompt, appName,
+                    mDevice.getProductName());
             mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
         }
         ap.mPositiveButtonText = getString(android.R.string.ok);
@@ -95,25 +106,123 @@
         ap.mPositiveButtonListener = this;
         ap.mNegativeButtonListener = this;
 
-        // add "always use" checkbox
-        LayoutInflater inflater = (LayoutInflater)getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-        ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
-        mAlwaysUse = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
-        if (mDevice == null) {
-            mAlwaysUse.setText(R.string.always_use_accessory);
-        } else {
-            mAlwaysUse.setText(R.string.always_use_device);
+        try {
+            PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName,
+                    PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+
+            if ((mDevice != null && canBeDefault(mDevice, packageInfo))
+                    || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) {
+                // add "open when" checkbox
+                LayoutInflater inflater = (LayoutInflater) getSystemService(
+                        Context.LAYOUT_INFLATER_SERVICE);
+                ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+                mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+                if (mDevice == null) {
+                    mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+                            mAccessory.getDescription()));
+                } else {
+                    mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+                            mDevice.getProductName()));
+                }
+                mAlwaysUse.setOnCheckedChangeListener(this);
+
+                mClearDefaultHint = (TextView)ap.mView.findViewById(
+                        com.android.internal.R.id.clearDefaultHint);
+                mClearDefaultHint.setVisibility(View.GONE);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // ignore
         }
-        mAlwaysUse.setOnCheckedChangeListener(this);
-        mClearDefaultHint = (TextView)ap.mView.findViewById(
-                                                    com.android.internal.R.id.clearDefaultHint);
-        mClearDefaultHint.setVisibility(View.GONE);
 
         setupAlert();
 
     }
 
+    /**
+     * Can the app be the default for the USB device. I.e. can the app be launched by default if
+     * the device is plugged in.
+     *
+     * @param device The device the app would be default for
+     * @param packageInfo The package info of the app
+     *
+     * @return {@code true} iff the app can be default
+     */
+    private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) {
+        ActivityInfo[] activities = packageInfo.activities;
+        if (activities != null) {
+            int numActivities = activities.length;
+            for (int i = 0; i < numActivities; i++) {
+                ActivityInfo activityInfo = activities[i];
+
+                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
+                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+                    if (parser == null) {
+                        continue;
+                    }
+
+                    XmlUtils.nextElement(parser);
+                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                        if ("usb-device".equals(parser.getName())) {
+                            DeviceFilter filter = DeviceFilter.read(parser);
+                            if (filter.matches(device)) {
+                                return true;
+                            }
+                        }
+
+                        XmlUtils.nextElement(parser);
+                    }
+                } catch (Exception e) {
+                    Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
+     * the accessory is plugged in.
+     *
+     * @param accessory The accessory the app would be default for
+     * @param packageInfo The package info of the app
+     *
+     * @return {@code true} iff the app can be default
+     */
+    private boolean canBeDefault(@NonNull UsbAccessory accessory,
+            @NonNull PackageInfo packageInfo) {
+        ActivityInfo[] activities = packageInfo.activities;
+        if (activities != null) {
+            int numActivities = activities.length;
+            for (int i = 0; i < numActivities; i++) {
+                ActivityInfo activityInfo = activities[i];
+
+                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
+                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+                    if (parser == null) {
+                        continue;
+                    }
+
+                    XmlUtils.nextElement(parser);
+                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                        if ("usb-accessory".equals(parser.getName())) {
+                            AccessoryFilter filter = AccessoryFilter.read(parser);
+                            if (filter.matches(accessory)) {
+                                return true;
+                            }
+                        }
+
+                        XmlUtils.nextElement(parser);
+                    }
+                } catch (Exception e) {
+                    Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+                }
+            }
+        }
+
+        return false;
+    }
+
     @Override
     public void onDestroy() {
         IBinder b = ServiceManager.getService(USB_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java b/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java
new file mode 100644
index 0000000..5ad8840
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.animation.TimeInterpolator;
+
+public class SystemUIInterpolators {
+    public static final class LogDecelerateInterpolator implements TimeInterpolator {
+        private final float mBase;
+        private final float mDrift;
+        private final float mTimeScale;
+        private final float mOutputScale;
+
+        public LogDecelerateInterpolator() {
+            this(400f, 1.4f, 0);
+        }
+
+        private LogDecelerateInterpolator(float base, float timeScale, float drift) {
+            mBase = base;
+            mDrift = drift;
+            mTimeScale = 1f / timeScale;
+
+            mOutputScale = 1f / computeLog(1f);
+        }
+
+        private float computeLog(float t) {
+            return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
+        }
+
+        @Override
+        public float getInterpolation(float t) {
+            return computeLog(t) * mOutputScale;
+        }
+    }
+
+    public static final class LogAccelerateInterpolator implements TimeInterpolator {
+        private final int mBase;
+        private final int mDrift;
+        private final float mLogScale;
+
+        public LogAccelerateInterpolator() {
+            this(100, 0);
+        }
+
+        private LogAccelerateInterpolator(int base, int drift) {
+            mBase = base;
+            mDrift = drift;
+            mLogScale = 1f / computeLog(1, mBase, mDrift);
+        }
+
+        private static float computeLog(float t, int base, int drift) {
+            return (float) -Math.pow(base, -t) + 1 + (drift * t);
+        }
+
+        @Override
+        public float getInterpolation(float t) {
+            return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
+        }
+    }
+
+    public interface Callback {
+        void onAnimatingChanged(boolean animating);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index ee8f18e..4dff9bd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -62,7 +62,6 @@
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
             | ActivityInfo.CONFIG_ASSETS_PATHS);
-    private final Extension mExtension;
     private VolumeDialog mDialog;
     private VolumePolicy mVolumePolicy = new VolumePolicy(
             DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT,  // volumeDownToEnterSilent
@@ -79,7 +78,7 @@
         // Allow plugins to reference the VolumeDialogController.
         Dependency.get(PluginDependencyProvider.class)
                 .allowPluginDependency(VolumeDialogController.class);
-        mExtension = Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
+        Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                 .withPlugin(VolumeDialog.class)
                 .withDefault(this::createDefault)
                 .withCallback(dialog -> {
@@ -151,7 +150,7 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         if (mConfigChanges.applyNewConfig(mContext.getResources())) {
-            mExtension.reload();
+            mController.mCallbacks.onConfigurationChanged();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 1ecaa13..4b8f581 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -19,6 +19,8 @@
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
 
+import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
@@ -26,11 +28,11 @@
 import android.app.Dialog;
 import android.app.KeyguardManager;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.media.AudioManager;
@@ -41,12 +43,10 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.provider.Settings.Global;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.view.ContextThemeWrapper;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
@@ -54,7 +54,6 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
@@ -68,12 +67,13 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.HardwareUiLayout;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.plugins.VolumeDialog;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.plugins.VolumeDialogController.State;
 import com.android.systemui.plugins.VolumeDialogController.StreamState;
-import com.android.systemui.statusbar.policy.ZenModeController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -89,8 +89,6 @@
 public class VolumeDialogImpl implements VolumeDialog {
     private static final String TAG = Util.logTag(VolumeDialogImpl.class);
 
-    public static final String SHOW_FULL_ZEN = "sysui_show_full_zen";
-
     private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
     private static final int UPDATE_ANIMATION_DURATION = 80;
 
@@ -99,6 +97,7 @@
     private final VolumeDialogController mController;
 
     private Window mWindow;
+    private HardwareUiLayout mHardwareLayout;
     private CustomDialog mDialog;
     private ViewGroup mDialogView;
     private ViewGroup mDialogRowsView;
@@ -107,16 +106,11 @@
     private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
     private final KeyguardManager mKeyguard;
-    private final AudioManager mAudioManager;
     private final AccessibilityManager mAccessibilityMgr;
-    private int mExpandButtonAnimationDuration;
     private final Object mSafetyWarningLock = new Object();
     private final Accessibility mAccessibility = new Accessibility();
     private final ColorStateList mActiveSliderTint;
     private final ColorStateList mInactiveSliderTint;
-    private VolumeDialogMotion mMotion;
-    private int mWindowType;
-    private final ZenModeController mZenModeController;
 
     private boolean mShowing;
     private boolean mShowA11yStream;
@@ -127,19 +121,12 @@
     private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
     private State mState;
     private SafetyWarningDialog mSafetyWarning;
-    private Callback mCallback;
-    private boolean mPendingStateChanged;
-    private boolean mPendingRecheckAll;
-    private long mCollapseTime;
     private boolean mHovering = false;
-    private int mDensity;
 
     public VolumeDialogImpl(Context context) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
-        mZenModeController = Dependency.get(ZenModeController.class);
         mController = Dependency.get(VolumeDialogController.class);
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         mAccessibilityMgr =
                 (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
         mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
@@ -147,18 +134,12 @@
     }
 
     public void init(int windowType, Callback callback) {
-        mCallback = callback;
-        mWindowType = windowType;
-
         initDialog();
 
         mAccessibility.init();
 
         mController.addCallback(mControllerCallbackH, mHandler);
         mController.getState();
-
-        final Configuration currentConfig = mContext.getResources().getConfiguration();
-        mDensity = currentConfig.densityDpi;
     }
 
     @Override
@@ -177,25 +158,16 @@
         mWindow = mDialog.getWindow();
         mWindow.requestFeature(Window.FEATURE_NO_TITLE);
         mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-        mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
-        mDialog.setCanceledOnTouchOutside(true);
-        final Resources res = mContext.getResources();
-        final WindowManager.LayoutParams lp = mWindow.getAttributes();
-        lp.type = mWindowType;
-        lp.format = PixelFormat.TRANSLUCENT;
-        lp.setTitle(VolumeDialogImpl.class.getSimpleName());
-        lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
-        lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top);
-        lp.gravity = Gravity.TOP;
-        lp.windowAnimations = -1;
-        mWindow.setAttributes(lp);
-        mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+        mWindow.addFlags(
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+        mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+        mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
 
         mDialog.setContentView(R.layout.volume_dialog);
         mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
@@ -209,26 +181,11 @@
                 return true;
             }
         });
+        mHardwareLayout = HardwareUiLayout.get(mDialogView);
+        mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
 
         mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
         mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows);
-        updateWindowWidthH();
-
-        mMotion = new VolumeDialogMotion(mDialog, mDialogView, mDialogContentView,
-                new VolumeDialogMotion.Callback() {
-                    @Override
-                    public void onAnimatingChanged(boolean animating) {
-                        if (animating) return;
-                        if (mPendingStateChanged) {
-                            mHandler.sendEmptyMessage(H.STATE_CHANGED);
-                            mPendingStateChanged = false;
-                        }
-                        if (mPendingRecheckAll) {
-                            mHandler.sendEmptyMessage(H.RECHECK_ALL);
-                            mPendingRecheckAll = false;
-                        }
-                    }
-                });
 
         if (mRows.isEmpty()) {
             addRow(AudioManager.STREAM_MUSIC,
@@ -250,28 +207,12 @@
         } else {
             addExistingRows();
         }
-        mExpandButtonAnimationDuration = res.getInteger(R.integer.volume_expand_animation_duration);
     }
 
     private ColorStateList loadColorStateList(int colorResId) {
         return ColorStateList.valueOf(mContext.getColor(colorResId));
     }
 
-    private void updateWindowWidthH() {
-        final ViewGroup.MarginLayoutParams lp =
-                (ViewGroup.MarginLayoutParams) mDialogView.getLayoutParams();
-        final DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
-        if (D.BUG) Log.d(TAG, "updateWindowWidth dm.w=" + dm.widthPixels);
-        int w = dm.widthPixels;
-        final int max = mContext.getResources()
-                .getDimensionPixelSize(R.dimen.volume_dialog_panel_width);
-        if (w > max) {
-            w = max;
-        }
-        lp.width = w - lp.getMarginEnd() - lp.getMarginStart();
-        mDialogView.setLayoutParams(lp);
-    }
-
     public void setStreamImportant(int stream, boolean important) {
         mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
     }
@@ -315,6 +256,7 @@
             final VolumeRow row = mRows.get(i);
             initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important);
             mDialogRowsView.addView(row.view);
+            updateVolumeRowH(row);
         }
     }
 
@@ -341,7 +283,6 @@
         writer.print("  mDynamic: "); writer.println(mDynamic);
         writer.print("  mAutomute: "); writer.println(mAutomute);
         writer.print("  mSilentMode: "); writer.println(mSilentMode);
-        writer.print("  mCollapseTime: "); writer.println(mCollapseTime);
         writer.print("  mAccessibility.mFeedbackEnabled: ");
         writer.println(mAccessibility.mFeedbackEnabled);
     }
@@ -364,9 +305,9 @@
         row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null);
         row.view.setId(row.stream);
         row.view.setTag(row);
-        row.header = (TextView) row.view.findViewById(R.id.volume_row_header);
+        row.header = row.view.findViewById(R.id.volume_row_header);
         row.header.setId(20 * row.stream);
-        row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider);
+        row.slider =  row.view.findViewById(R.id.volume_row_slider);
         row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
         row.anim = null;
 
@@ -447,11 +388,27 @@
         rescheduleTimeoutH();
         if (mShowing) return;
         mShowing = true;
-        mMotion.startShow();
+        mHardwareLayout.setTranslationX(getAnimTranslation());
+        mHardwareLayout.setAlpha(0);
+        mHardwareLayout.animate()
+                .alpha(1)
+                .translationX(0)
+                .setDuration(300)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .withEndAction(() -> {
+                    mDialog.show();
+                    mWindow.getDecorView().requestAccessibilityFocus();
+                })
+                .start();
         Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
         mController.notifyVisible(true);
     }
 
+    private float getAnimTranslation() {
+        return mContext.getResources().getDimension(
+                R.dimen.volume_dialog_panel_width) / 2;
+    }
+
     protected void rescheduleTimeoutH() {
         mHandler.removeMessages(H.DISMISS);
         final int timeout = computeTimeoutH();
@@ -470,14 +427,19 @@
     }
 
     protected void dismissH(int reason) {
-        if (mMotion.isAnimating()) {
-            return;
-        }
         mHandler.removeMessages(H.DISMISS);
         mHandler.removeMessages(H.SHOW);
         if (!mShowing) return;
         mShowing = false;
-        mMotion.startDismiss();
+        mHardwareLayout.setTranslationX(0);
+        mHardwareLayout.setAlpha(1);
+        mHardwareLayout.animate()
+                .alpha(0)
+                .translationX(getAnimTranslation())
+                .setDuration(300)
+                .withEndAction(() -> mDialog.dismiss())
+                .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+                .start();
         if (mAccessibilityMgr.isEnabled()) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -497,30 +459,6 @@
         }
     }
 
-    private void updateDialogBottomMarginH() {
-        final long diff = System.currentTimeMillis() - mCollapseTime;
-        final boolean collapsing = mCollapseTime != 0 && diff < getConservativeCollapseDuration();
-        final ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) mDialogView.getLayoutParams();
-        final int bottomMargin = collapsing ? mDialogContentView.getHeight() :
-                mContext.getResources().getDimensionPixelSize(R.dimen.volume_dialog_margin_bottom);
-        if (bottomMargin != mlp.bottomMargin) {
-            if (D.BUG) Log.d(TAG, "bottomMargin " + mlp.bottomMargin + " -> " + bottomMargin);
-            mlp.bottomMargin = bottomMargin;
-            mDialogView.setLayoutParams(mlp);
-        }
-    }
-
-    private long getConservativeCollapseDuration() {
-        return mExpandButtonAnimationDuration * 3;
-    }
-
-    private void prepareForCollapse() {
-        mHandler.removeMessages(H.UPDATE_BOTTOM_MARGIN);
-        mCollapseTime = System.currentTimeMillis();
-        updateDialogBottomMarginH();
-        mHandler.sendEmptyMessageDelayed(H.UPDATE_BOTTOM_MARGIN, getConservativeCollapseDuration());
-    }
-
     private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
         boolean isActive = row == activeRow;
         if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
@@ -567,13 +505,7 @@
     }
 
     private void onStateChangedH(State state) {
-        final boolean animating = mMotion.isAnimating();
-        if (D.BUG) Log.d(TAG, "onStateChangedH animating=" + animating);
         mState = state;
-        if (animating) {
-            mPendingStateChanged = true;
-            return;
-        }
         mDynamic.clear();
         // add any new dynamic rows
         for (int i = 0; i < state.states.size(); i++) {
@@ -642,19 +574,13 @@
         row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
         final int iconRes =
                 isRingVibrate ? R.drawable.ic_volume_ringer_vibrate
-                : isRingSilent || zenMuted ? row.cachedIconRes
+                : isRingSilent || zenMuted ? row.iconMuteRes
                 : ss.routedToBluetooth ?
                         (ss.muted ? R.drawable.ic_volume_media_bt_mute
                                 : R.drawable.ic_volume_media_bt)
                 : mAutomute && ss.level == 0 ? row.iconMuteRes
                 : (ss.muted ? row.iconMuteRes : row.iconRes);
-        if (iconRes != row.cachedIconRes) {
-            if (row.cachedIconRes != 0 && isRingVibrate) {
-                mController.vibrate();
-            }
-            row.cachedIconRes = iconRes;
-            row.icon.setImageResource(iconRes);
-        }
+        row.icon.setImageResource(iconRes);
         row.iconState =
                 iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
                 : (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes)
@@ -864,14 +790,8 @@
 
         @Override
         public void onConfigurationChanged() {
-            Configuration newConfig = mContext.getResources().getConfiguration();
-            final int density = newConfig.densityDpi;
-            if (density != mDensity) {
-                mDialog.dismiss();
-                initDialog();
-                mDensity = density;
-            }
-            updateWindowWidthH();
+            mDialog.dismiss();
+            initDialog();
             mConfigurableTexts.update();
         }
 
@@ -908,23 +828,6 @@
         }
     };
 
-    private final ZenModePanel.Callback mZenPanelCallback = new ZenModePanel.Callback() {
-        @Override
-        public void onPrioritySettings() {
-            mCallback.onZenPrioritySettingsClicked();
-        }
-
-        @Override
-        public void onInteraction() {
-            mHandler.sendEmptyMessage(H.RESCHEDULE_TIMEOUT);
-        }
-
-        @Override
-        public void onExpanded(boolean expanded) {
-            // noop.
-        }
-    };
-
     private final class H extends Handler {
         private static final int SHOW = 1;
         private static final int DISMISS = 2;
@@ -933,7 +836,6 @@
         private static final int SET_STREAM_IMPORTANT = 5;
         private static final int RESCHEDULE_TIMEOUT = 6;
         private static final int STATE_CHANGED = 7;
-        private static final int UPDATE_BOTTOM_MARGIN = 8;
 
         public H() {
             super(Looper.getMainLooper());
@@ -949,14 +851,13 @@
                 case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
                 case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
                 case STATE_CHANGED: onStateChangedH(mState); break;
-                case UPDATE_BOTTOM_MARGIN: updateDialogBottomMarginH(); break;
             }
         }
     }
 
-    private final class CustomDialog extends Dialog {
+    private final class CustomDialog extends Dialog implements DialogInterface {
         public CustomDialog(Context context) {
-            super(context);
+            super(context, com.android.systemui.R.style.qs_theme);
         }
 
         @Override
@@ -966,26 +867,15 @@
         }
 
         @Override
-        protected void onStop() {
-            super.onStop();
-            final boolean animating = mMotion.isAnimating();
-            if (D.BUG) Log.d(TAG, "onStop animating=" + animating);
-            if (animating) {
-                mPendingRecheckAll = true;
-                return;
-            }
-            mHandler.sendEmptyMessage(H.RECHECK_ALL);
+        protected void onStart() {
+            super.setCanceledOnTouchOutside(true);
+            super.onStart();
         }
 
         @Override
-        public boolean onTouchEvent(MotionEvent event) {
-            if (isShowing()) {
-                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-                    dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
-                    return true;
-                }
-            }
-            return false;
+        protected void onStop() {
+            super.onStop();
+            mHandler.sendEmptyMessage(H.RECHECK_ALL);
         }
 
         @Override
@@ -1128,7 +1018,6 @@
         private int iconRes;
         private int iconMuteRes;
         private boolean important;
-        private int cachedIconRes;
         private ColorStateList cachedSliderTint;
         private int iconState;  // from Events
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
deleted file mode 100644
index 2b65fbd..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.DialogInterface.OnShowListener;
-import android.os.Handler;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.PathInterpolator;
-
-public class VolumeDialogMotion {
-    private static final String TAG = Util.logTag(VolumeDialogMotion.class);
-
-    private static final float ANIMATION_SCALE = 1.0f;
-    private static final int PRE_DISMISS_DELAY = 50;
-
-    private final Dialog mDialog;
-    private final View mDialogView;
-    private final ViewGroup mContents;  // volume rows + zen footer
-    private final Handler mHandler = new Handler();
-    private final Callback mCallback;
-
-    private boolean mAnimating;  // show or dismiss animation is running
-    private boolean mShowing;  // show animation is running
-    private boolean mDismissing;  // dismiss animation is running
-    private ValueAnimator mContentsPositionAnimator;
-
-    public VolumeDialogMotion(Dialog dialog, View dialogView, ViewGroup contents,
-            Callback callback) {
-        mDialog = dialog;
-        mDialogView = dialogView;
-        mContents = contents;
-        mCallback = callback;
-        mDialog.setOnDismissListener(new OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                if (D.BUG) Log.d(TAG, "mDialog.onDismiss");
-            }
-        });
-        mDialog.setOnShowListener(new OnShowListener() {
-            @Override
-            public void onShow(DialogInterface dialog) {
-                if (D.BUG) Log.d(TAG, "mDialog.onShow");
-                final int h = mDialogView.getHeight();
-                mDialogView.setTranslationY(-h);
-                startShowAnimation();
-            }
-        });
-    }
-
-    public boolean isAnimating() {
-        return mAnimating;
-    }
-
-    private void setShowing(boolean showing) {
-        if (showing == mShowing) return;
-        mShowing = showing;
-        if (D.BUG) Log.d(TAG, "mShowing = " + mShowing);
-        updateAnimating();
-    }
-
-    private void setDismissing(boolean dismissing) {
-        if (dismissing == mDismissing) return;
-        mDismissing = dismissing;
-        if (D.BUG) Log.d(TAG, "mDismissing = " + mDismissing);
-        updateAnimating();
-    }
-
-    private void updateAnimating() {
-        final boolean animating = mShowing || mDismissing;
-        if (animating == mAnimating) return;
-        mAnimating = animating;
-        if (D.BUG) Log.d(TAG, "mAnimating = " + mAnimating);
-        if (mCallback != null) {
-            mCallback.onAnimatingChanged(mAnimating);
-        }
-    }
-
-    public void startShow() {
-        if (D.BUG) Log.d(TAG, "startShow");
-        if (mShowing) return;
-        setShowing(true);
-        if (mDismissing) {
-            mDialogView.animate().cancel();
-            setDismissing(false);
-            startShowAnimation();
-            return;
-        }
-        if (D.BUG) Log.d(TAG, "mDialog.show()");
-        mDialog.show();
-    }
-
-    private void startShowAnimation() {
-        if (D.BUG) Log.d(TAG, "startShowAnimation");
-        mDialogView.animate()
-                .translationY(0)
-                .setDuration(scaledDuration(300))
-                .setInterpolator(new LogDecelerateInterpolator())
-                .setListener(null)
-                .start();
-
-        mContentsPositionAnimator = ValueAnimator.ofFloat(0).setDuration(scaledDuration(400));
-        mContentsPositionAnimator.addListener(new AnimatorListenerAdapter() {
-            private boolean mCancelled;
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (mCancelled) return;
-                if (D.BUG) Log.d(TAG, "show.onAnimationEnd");
-                setShowing(false);
-            }
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                if (D.BUG) Log.d(TAG, "show.onAnimationCancel");
-                mCancelled = true;
-            }
-        });
-        mContentsPositionAnimator.addUpdateListener(new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float v = (Float) animation.getAnimatedValue();
-                mContents.setTranslationY(v + -mDialogView.getTranslationY());
-            }
-        });
-        mContentsPositionAnimator.setInterpolator(new LogDecelerateInterpolator());
-        mContentsPositionAnimator.start();
-
-        mContents.setAlpha(0);
-        mContents.animate()
-                .alpha(1)
-                .setDuration(scaledDuration(150))
-                .setInterpolator(new PathInterpolator(0f, 0f, .2f, 1f))
-                .start();
-    }
-
-    public void startDismiss() {
-        if (D.BUG) Log.d(TAG, "startDismiss");
-        if (mDismissing) return;
-        setDismissing(true);
-        if (mShowing) {
-            mDialogView.animate().cancel();
-            if (mContentsPositionAnimator != null) {
-                mContentsPositionAnimator.cancel();
-            }
-            mContents.animate().cancel();
-            setShowing(false);
-        }
-        mDialogView.animate()
-                .translationY(-mDialogView.getHeight())
-                .setDuration(scaledDuration(250))
-                .setInterpolator(new LogAccelerateInterpolator())
-                .setUpdateListener(new AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        mContents.setTranslationY(-mDialogView.getTranslationY());
-                    }
-                })
-                .setListener(new AnimatorListenerAdapter() {
-                    private boolean mCancelled;
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        if (mCancelled) return;
-                        if (D.BUG) Log.d(TAG, "dismiss.onAnimationEnd");
-                        mHandler.postDelayed(new Runnable() {
-                            @Override
-                            public void run() {
-                                if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
-                                mDialog.dismiss();
-                                setDismissing(false);
-                            }
-                        }, PRE_DISMISS_DELAY);
-
-                    }
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        if (D.BUG) Log.d(TAG, "dismiss.onAnimationCancel");
-                        mCancelled = true;
-                    }
-                }).start();
-    }
-
-    private static int scaledDuration(int base) {
-        return (int) (base * ANIMATION_SCALE);
-    }
-
-    public static final class LogDecelerateInterpolator implements TimeInterpolator {
-        private final float mBase;
-        private final float mDrift;
-        private final float mTimeScale;
-        private final float mOutputScale;
-
-        public LogDecelerateInterpolator() {
-            this(400f, 1.4f, 0);
-        }
-
-        private LogDecelerateInterpolator(float base, float timeScale, float drift) {
-            mBase = base;
-            mDrift = drift;
-            mTimeScale = 1f / timeScale;
-
-            mOutputScale = 1f / computeLog(1f);
-        }
-
-        private float computeLog(float t) {
-            return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
-        }
-
-        @Override
-        public float getInterpolation(float t) {
-            return computeLog(t) * mOutputScale;
-        }
-    }
-
-    public static final class LogAccelerateInterpolator implements TimeInterpolator {
-        private final int mBase;
-        private final int mDrift;
-        private final float mLogScale;
-
-        public LogAccelerateInterpolator() {
-            this(100, 0);
-        }
-
-        private LogAccelerateInterpolator(int base, int drift) {
-            mBase = base;
-            mDrift = drift;
-            mLogScale = 1f / computeLog(1, mBase, mDrift);
-        }
-
-        private static float computeLog(float t, int base, int drift) {
-            return (float) -Math.pow(base, -t) + 1 + (drift * t);
-        }
-
-        @Override
-        public float getInterpolation(float t) {
-            return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
-        }
-    }
-
-    public interface Callback {
-        void onAnimatingChanged(boolean animating);
-    }
-}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 67fae5b..e74736a 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -43,6 +43,9 @@
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.STATUS_BAR" />
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 65d9699..fbcbd20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -23,6 +23,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
+import android.os.ParcelFileDescriptor;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.testing.LeakCheck;
@@ -34,6 +35,8 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 
@@ -84,6 +87,16 @@
         return mContext;
     }
 
+    protected void runShellCommand(String command) throws IOException {
+        ParcelFileDescriptor pfd = mRealInstrumentation.getUiAutomation()
+                .executeShellCommand(command);
+
+        // Read the input stream fully.
+        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+        while (fis.read() != -1);
+        fis.close();
+    }
+
     protected void waitForIdleSync() {
         if (mHandler == null) {
             mHandler = new Handler(Looper.getMainLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
new file mode 100644
index 0000000..bdbd244
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.recents;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+
+import static com.android.systemui.recents.RecentsImpl.RECENTS_ACTIVITY;
+import static com.android.systemui.recents.RecentsImpl.RECENTS_PACKAGE;
+
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.IActivityManager;
+import android.os.SystemClock;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class RecentsTest extends SysuiTestCase {
+
+    @Test
+    public void testRecentsActivityType() throws Exception {
+        // Clear the state
+        final IActivityManager am = ActivityManager.getService();
+        am.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_RECENTS });
+
+        // Toggle recents, use a shell command because it is not exported
+        runShellCommand("am start -n " + RECENTS_PACKAGE + "/" + RECENTS_ACTIVITY);
+
+        // Verify that an activity was launched with the right activity type
+        int retryCount = 0;
+        while (retryCount < 10) {
+            List<RunningTaskInfo> tasks = am.getTasks(Integer.MAX_VALUE);
+            for (RunningTaskInfo info : tasks) {
+                if (info.configuration.windowConfiguration.getActivityType()
+                        == ACTIVITY_TYPE_RECENTS) {
+                    // Found a recents activity with the right activity type
+                    return;
+                }
+            }
+            SystemClock.sleep(50);
+            retryCount++;
+        }
+        fail("Expected Recents activity with ACTIVITY_TYPE_RECENTS");
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 88fa659..6721938 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -745,7 +745,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
                 mNotificationChannel.getImportance(), mSbn, null, null, null,
-                (Runnable saveImportance) -> {
+                (Runnable saveImportance, StatusBarNotification sbn) -> {
                 },
                 Collections.singleton(TEST_PACKAGE_NAME));
 
@@ -762,7 +762,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
                 mNotificationChannel.getImportance(), mSbn, null, null, null,
-                (Runnable saveImportance) -> {
+                (Runnable saveImportance, StatusBarNotification sbn) -> {
                     saveImportance.run();
                 },
                 Collections.singleton(TEST_PACKAGE_NAME));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 3b401a5..a95e3db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -24,7 +24,7 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
@@ -61,49 +61,49 @@
 
     @Test
     public void nightTileAdded_whenActivated() {
-        if (!NightDisplayController.isAvailable(mContext)) {
+        if (!ColorDisplayController.isAvailable(mContext)) {
             return;
         }
-        mAutoTileManager.mNightDisplayCallback.onActivated(true);
+        mAutoTileManager.mColorDisplayCallback.onActivated(true);
         verify(mQsTileHost).addTile("night");
     }
 
     @Test
     public void nightTileNotAdded_whenDeactivated() {
-        if (!NightDisplayController.isAvailable(mContext)) {
+        if (!ColorDisplayController.isAvailable(mContext)) {
             return;
         }
-        mAutoTileManager.mNightDisplayCallback.onActivated(false);
+        mAutoTileManager.mColorDisplayCallback.onActivated(false);
         verify(mQsTileHost, never()).addTile("night");
     }
 
     @Test
     public void nightTileAdded_whenNightModeTwilight() {
-        if (!NightDisplayController.isAvailable(mContext)) {
+        if (!ColorDisplayController.isAvailable(mContext)) {
             return;
         }
-        mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
-                NightDisplayController.AUTO_MODE_TWILIGHT);
+        mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+                ColorDisplayController.AUTO_MODE_TWILIGHT);
         verify(mQsTileHost).addTile("night");
     }
 
     @Test
     public void nightTileAdded_whenNightModeCustom() {
-        if (!NightDisplayController.isAvailable(mContext)) {
+        if (!ColorDisplayController.isAvailable(mContext)) {
             return;
         }
-        mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
-                NightDisplayController.AUTO_MODE_CUSTOM);
+        mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+                ColorDisplayController.AUTO_MODE_CUSTOM);
         verify(mQsTileHost).addTile("night");
     }
 
     @Test
     public void nightTileNotAdded_whenNightModeDisabled() {
-        if (!NightDisplayController.isAvailable(mContext)) {
+        if (!ColorDisplayController.isAvailable(mContext)) {
             return;
         }
-        mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
-                NightDisplayController.AUTO_MODE_DISABLED);
+        mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+                ColorDisplayController.AUTO_MODE_DISABLED);
         verify(mQsTileHost, never()).addTile("night");
     }
 }
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index 327f17d..0e39446 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -489,7 +489,7 @@
 
 // Represents statistics from NFLOG wakeup events due to ingress packets.
 // Since oc-mr1.
-// Next tag: 8.
+// Next tag: 13.
 message WakeupStats {
   // The time duration in seconds covered by these stats, for deriving
   // exact wakeup rates.
@@ -517,6 +517,24 @@
 
   // The total number of wakeup packets with no associated socket or uid.
   optional int64 no_uid_wakeups = 7;
+
+  // Counts of all different ethertype values from wakeup packets received.
+  repeated Pair ethertype_counts = 8;
+
+  // Counts of all different IP next header values from wakeup packets received.
+  repeated Pair ip_next_header_counts = 9;
+
+  // The total number of wakeup packets whose destination hardware address was
+  // a unicast address.
+  optional int64 l2_unicast_count = 10;
+
+  // The total number of wakeup packets whose destination hardware address was
+  // a multicast address.
+  optional int64 l2_multicast_count = 11;
+
+  // The total number of wakeup packets whose destination hardware address was
+  // a broadcast address.
+  optional int64 l2_broadcast_count = 12;
 }
 
 // Represents one of the IP connectivity event defined in this file.
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d3dcf5a..73a6d7c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -171,6 +171,12 @@
     TEXT_CLASSIFIER_TYPE_URL = 6;
   }
 
+  // Selection invocation methods. Used as sub-type for TEXT_SELECTION_SESSION events.
+  enum TextSelectionInvocationMethod {
+    TEXT_SELECTION_INVOCATION_MANUAL = 1;
+    TEXT_SELECTION_INVOCATION_LINK = 2;
+  }
+
   // Known visual elements: views or controls.
   enum View {
     // Unknown view
@@ -4680,6 +4686,78 @@
     // OS: P
     DIALOG_FIRMWARE_VERSION = 1247;
 
+    // OPEN: Settings > Network & internet > Menu > Private DNS
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_PRIVATE_DNS = 1248;
+
+    // ACTION: A private dns mode been selected by user
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_PRIVATE_DNS_MODE = 1249;
+
+    // FIELD: text select start offset in words (as defined by the ICU BreakIterator).
+    // CATEGORY: TEXT_SELECTION_SESSION
+    // OS: P
+    FIELD_SELECTION_RANGE_START = 1250;
+
+    // FIELD: text select end offset in words (as defined by the ICU BreakIterator).
+    // CATEGORY: TEXT_SELECTION_SESSION
+    // OS: P
+    FIELD_SELECTION_RANGE_END = 1251;
+
+    // FIELD: smart text selection start offset in words (as defined by the ICU BreakIterator),
+    //        stored as two packed 16bit integers. (start in MSBs, end in LSBs)
+    // CATEGORY: TEXT_SELECTION_SESSION
+    // OS: P
+    FIELD_SELECTION_SMART_RANGE_START = 1252;
+
+    // FIELD: smart text selection end offset in words (as defined by the ICU BreakIterator),
+    //        stored as two packed 16bit integers. (start in MSBs, end in LSBs)
+    // CATEGORY: TEXT_SELECTION_SESSION
+    // OS: P
+    FIELD_SELECTION_SMART_RANGE_END = 1253;
+
+    // FIELD: the entity type of the text currently selected.
+    // CATEGORY: TEXT_SELECTION_SESSION
+    // OS: P
+    FIELD_SELECTION_ENTITY_TYPE = 1254;
+
+    // FIELD: the type of widget the selection was made in.
+    // CATEGORY: TEXT_SELECTION_SESSION
+    // OS: P
+    FIELD_SELECTION_WIDGET_TYPE = 1255;
+
+    // FIELD: the name of the text classifier model used.
+    // CATEGORY: TEXT_SELECTION_SESSION
+    // OS: P
+    FIELD_TEXTCLASSIFIER_MODEL = 1256;
+
+    // OPEN: Settings > Sound & notification > Do Not Disturb > Behavior > Messages
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_MESSAGES = 1257;
+
+    // OPEN: Settings > Sound & notification > Do Not Disturb > Behavior > Calls
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_CALLS = 1258;
+
+    // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Until you turn off
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_TOGGLE_ON_FOREVER = 1259;
+
+    // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Time countdown manual rule (ie: for one hour)
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_TOGGLE_ON_COUNTDOWN = 1260;
+
+    // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Next Alarm (ie: Until Tue 7:20 AM)
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_TOGGLE_ON_ALARM = 1261;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 9d25055..8f58a38 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -364,6 +364,12 @@
   // (one value added per unique ESS - potentially multiple counts per single
   // scan!)
   repeated NumConnectableNetworksBucket observed_hotspot_r2_aps_per_ess_in_scan_histogram = 88;
+
+  // SoftAP event list tracking sessions and client counts in tethered mode
+  repeated SoftApConnectedClientsEvent soft_ap_connected_clients_events_tethered = 89;
+
+  // SoftAP event list tracking sessions and client counts in local only mode
+  repeated SoftApConnectedClientsEvent soft_ap_connected_clients_events_local_only = 90;
 }
 
 // Information that gets logged for every WiFi connection.
@@ -1071,3 +1077,29 @@
   // Occurrences of this action.
   optional int32 count = 4;
 }
+
+// SoftAP event tracking sessions and client counts
+message SoftApConnectedClientsEvent {
+
+  // Soft AP event Types
+  enum SoftApEventType {
+
+    // Soft AP is Up and ready for use
+    SOFT_AP_UP = 0;
+
+    // Soft AP is Down
+    SOFT_AP_DOWN = 1;
+
+    // Number of connected soft AP clients has changed
+    NUM_CLIENTS_CHANGED = 2;
+  }
+
+  // Type of event being recorded
+  optional SoftApEventType event_type = 1;
+
+  // Absolute time when event happened
+  optional int64 time_stamp_millis = 2;
+
+  // Number of connected clients if event_type is NUM_CLIENTS_CHANGED, otherwise zero.
+  optional int32 num_connected_clients = 3;
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 06c110d..d661754 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1318,7 +1318,7 @@
 
     private void unbindAllServicesLocked(UserState userState) {
         List<AccessibilityServiceConnection> services = userState.mBoundServices;
-        for (int count = services.size(); count > 1; count--) {
+        for (int count = services.size(); count > 0; count--) {
             // When the service is unbound, it disappears from the list, so there's no need to
             // keep track of the index
             services.get(0).unbindLocked();
diff --git a/services/art-profile b/services/art-profile
index ac5ecaf..301918f 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2374,6 +2374,9 @@
 HPLcom/android/server/display/AutomaticBrightnessController;->updateAmbientLux()V
 HPLcom/android/server/display/AutomaticBrightnessController;->updateAmbientLux(J)V
 HPLcom/android/server/display/AutomaticBrightnessController;->weightIntegral(J)F
+HPLcom/android/server/display/ColorDisplayService$3;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V
+HPLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;->evaluate(FLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+HPLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;->evaluate(F[F[F)[F
 HPLcom/android/server/display/DisplayBlanker;->requestDisplayState(II)V
 HPLcom/android/server/display/DisplayManagerService$BinderService;->getDisplayIds()[I
 HPLcom/android/server/display/DisplayManagerService$CallbackRecord;->binderDied()V
@@ -2430,9 +2433,6 @@
 HPLcom/android/server/display/LogicalDisplay;->getRequestedModeIdLocked()I
 HPLcom/android/server/display/LogicalDisplay;->hasContentLocked()Z
 HPLcom/android/server/display/LogicalDisplay;->setDisplayInfoOverrideFromWindowManagerLocked(Landroid/view/DisplayInfo;)Z
-HPLcom/android/server/display/NightDisplayService$3;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V
-HPLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;->evaluate(FLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;->evaluate(F[F[F)[F
 HPLcom/android/server/display/RampAnimator$1;->run()V
 HPLcom/android/server/display/RampAnimator$Listener;->onAnimationEnd()V
 HPLcom/android/server/display/RampAnimator;->-get0(Lcom/android/server/display/RampAnimator;)F
@@ -10133,6 +10133,25 @@
 PLcom/android/server/display/AutomaticBrightnessController;->setLightSensorEnabled(Z)Z
 PLcom/android/server/display/AutomaticBrightnessController;->setScreenAutoBrightnessAdjustment(F)Z
 PLcom/android/server/display/AutomaticBrightnessController;->updateAutoBrightness(Z)V
+PLcom/android/server/display/ColorDisplayService$1;-><init>(Lcom/android/server/display/ColorDisplayService;)V
+PLcom/android/server/display/ColorDisplayService$3;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/DisplayTransformManager;)V
+PLcom/android/server/display/ColorDisplayService$4;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/DisplayTransformManager;[F)V
+PLcom/android/server/display/ColorDisplayService$4;->onAnimationCancel(Landroid/animation/Animator;)V
+PLcom/android/server/display/ColorDisplayService$4;->onAnimationEnd(Landroid/animation/Animator;)V
+PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;-><init>()V
+PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;-><init>(Lcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;)V
+PLcom/android/server/display/ColorDisplayService;->-set0(Lcom/android/server/display/ColorDisplayService;Landroid/animation/ValueAnimator;)Landroid/animation/ValueAnimator;
+PLcom/android/server/display/ColorDisplayService;-><init>(Landroid/content/Context;)V
+PLcom/android/server/display/ColorDisplayService;->applyTint(Z)V
+PLcom/android/server/display/ColorDisplayService;->isUserSetupCompleted(Landroid/content/ContentResolver;I)Z
+PLcom/android/server/display/ColorDisplayService;->onActivated(Z)V
+PLcom/android/server/display/ColorDisplayService;->onAutoModeChanged(I)V
+PLcom/android/server/display/ColorDisplayService;->onBootPhase(I)V
+PLcom/android/server/display/ColorDisplayService;->onStart()V
+PLcom/android/server/display/ColorDisplayService;->onStartUser(I)V
+PLcom/android/server/display/ColorDisplayService;->onUserChanged(I)V
+PLcom/android/server/display/ColorDisplayService;->setMatrix(I[F)V
+PLcom/android/server/display/ColorDisplayService;->setUp()V
 PLcom/android/server/display/ColorFade;-><init>(I)V
 PLcom/android/server/display/ColorFade;->createNativeFloatBuffer(I)Ljava/nio/FloatBuffer;
 PLcom/android/server/display/ColorFade;->dismiss()V
@@ -10230,25 +10249,6 @@
 PLcom/android/server/display/LogicalDisplay;->getDisplayIdLocked()I
 PLcom/android/server/display/LogicalDisplay;->getNonOverrideDisplayInfoLocked(Landroid/view/DisplayInfo;)V
 PLcom/android/server/display/LogicalDisplay;->setHasContentLocked(Z)V
-PLcom/android/server/display/NightDisplayService$1;-><init>(Lcom/android/server/display/NightDisplayService;)V
-PLcom/android/server/display/NightDisplayService$3;-><init>(Lcom/android/server/display/NightDisplayService;Lcom/android/server/display/DisplayTransformManager;)V
-PLcom/android/server/display/NightDisplayService$4;-><init>(Lcom/android/server/display/NightDisplayService;Lcom/android/server/display/DisplayTransformManager;[F)V
-PLcom/android/server/display/NightDisplayService$4;->onAnimationCancel(Landroid/animation/Animator;)V
-PLcom/android/server/display/NightDisplayService$4;->onAnimationEnd(Landroid/animation/Animator;)V
-PLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;-><init>()V
-PLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;-><init>(Lcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;)V
-PLcom/android/server/display/NightDisplayService;->-set0(Lcom/android/server/display/NightDisplayService;Landroid/animation/ValueAnimator;)Landroid/animation/ValueAnimator;
-PLcom/android/server/display/NightDisplayService;-><init>(Landroid/content/Context;)V
-PLcom/android/server/display/NightDisplayService;->applyTint(Z)V
-PLcom/android/server/display/NightDisplayService;->isUserSetupCompleted(Landroid/content/ContentResolver;I)Z
-PLcom/android/server/display/NightDisplayService;->onActivated(Z)V
-PLcom/android/server/display/NightDisplayService;->onAutoModeChanged(I)V
-PLcom/android/server/display/NightDisplayService;->onBootPhase(I)V
-PLcom/android/server/display/NightDisplayService;->onStart()V
-PLcom/android/server/display/NightDisplayService;->onStartUser(I)V
-PLcom/android/server/display/NightDisplayService;->onUserChanged(I)V
-PLcom/android/server/display/NightDisplayService;->setMatrix(I[F)V
-PLcom/android/server/display/NightDisplayService;->setUp()V
 PLcom/android/server/display/OverlayDisplayAdapter$1$1;-><init>(Lcom/android/server/display/OverlayDisplayAdapter$1;Landroid/os/Handler;)V
 PLcom/android/server/display/OverlayDisplayAdapter$1;-><init>(Lcom/android/server/display/OverlayDisplayAdapter;)V
 PLcom/android/server/display/OverlayDisplayAdapter$1;->run()V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index d8eaccc..a3def14 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -602,7 +602,7 @@
             if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
-                                null, null, null, null));
+                                null, null, null, null, null, -1));
             }
         }
     }
@@ -616,7 +616,7 @@
             if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
-                                clientState, null, null, null, null, null, null));
+                                clientState, null, null, null, null, null, null, null, -1));
             }
         }
     }
@@ -628,7 +628,7 @@
         synchronized (mLock) {
             if (isValidEventLocked("logSaveShown()", sessionId)) {
                 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
-                        null, null, null, null, null));
+                        null, null, null, null, null, null, -1));
             }
         }
     }
@@ -642,7 +642,7 @@
             if (isValidEventLocked("logDatasetSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
-                                null, null, null, null, null));
+                                null, null, null, null, null, null, -1));
             }
         }
     }
@@ -656,13 +656,15 @@
             @Nullable ArrayList<AutofillId> changedFieldIds,
             @Nullable ArrayList<String> changedDatasetIds,
             @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
-            @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds) {
+            @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
+            @Nullable String detectedRemoteId, int detectedFieldScore) {
         synchronized (mLock) {
             if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
                 mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
                         clientState, selectedDatasets, ignoredDatasets,
                         changedFieldIds, changedDatasetIds,
-                        manuallyFilledFieldIds, manuallyFilledDatasetIds));
+                        manuallyFilledFieldIds, manuallyFilledDatasetIds,
+                        detectedRemoteId, detectedFieldScore));
             }
         }
     }
@@ -695,6 +697,7 @@
         pw.print(prefix); pw.print("Default component: ");
             pw.println(mContext.getString(R.string.config_defaultAutofillService));
         pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+        pw.print(prefix); pw.print("Field detection: "); pw.println(isFieldDetectionEnabled());
         pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
@@ -935,6 +938,13 @@
         return false;
     }
 
+    // TODO(b/67867469): remove once feature is finished
+    boolean isFieldDetectionEnabled() {
+        return Settings.Secure.getIntForUser(
+                mContext.getContentResolver(), Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 0,
+                mUserId) == 1;
+    }
+
     @Override
     public String toString() {
         return "AutofillManagerServiceImpl: [userId=" + mUserId
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3564432..5823ab1 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -54,6 +54,7 @@
 import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
+import android.service.autofill.FieldsDetection;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -492,6 +493,13 @@
             }
         }
 
+        // TODO(b/67867469): remove once feature is finished
+        if (response.getFieldsDetection() != null && !mService.isFieldDetectionEnabled()) {
+            Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
+            processNullResponseLocked(requestFlags);
+            return;
+        }
+
         mService.setLastResponse(serviceUid, id, response);
 
         int sessionFinishedState = 0;
@@ -913,11 +921,29 @@
                 }
             }
         }
-        if (!hasAtLeastOneDataset) {
-            if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): skipped (no datasets)");
+        final FieldsDetection fieldsDetection = lastResponse.getFieldsDetection();
+
+        if (!hasAtLeastOneDataset && fieldsDetection == null) {
+            if (sVerbose) {
+                Slog.v(TAG, "logContextCommittedLocked(): skipped (no datasets nor fields "
+                        + "detection)");
+            }
             return;
         }
 
+        final AutofillId detectableFieldId;
+        final String detectableRemoteId;
+        String detectedRemoteId = null;
+        if (fieldsDetection == null) {
+            detectableFieldId = null;
+            detectableRemoteId = null;
+        } else {
+            detectableFieldId = fieldsDetection.getFieldId();
+            detectableRemoteId = fieldsDetection.getRemoteId();
+        }
+
+        int detectedFieldScore = -1;
+
         for (int i = 0; i < mViewStates.size(); i++) {
             final ViewState viewState = mViewStates.valueAt(i);
             final int state = viewState.getState();
@@ -926,7 +952,6 @@
             // - autofilled -> changedDatasetIds
             // - not autofilled but matches a dataset value -> manuallyFilledIds
             if ((state & ViewState.STATE_CHANGED) != 0) {
-
                 // Check if autofilled value was changed
                 if ((state & ViewState.STATE_AUTOFILLED) != 0) {
                     final String datasetId = viewState.getDatasetId();
@@ -958,7 +983,6 @@
                     changedFieldIds.add(viewState.id);
                     changedDatasetIds.add(datasetId);
                 } else {
-                    // Check if value match a dataset.
                     final AutofillValue currentValue = viewState.getCurrentValue();
                     if (currentValue == null) {
                         if (sDebug) {
@@ -967,58 +991,78 @@
                         }
                         continue;
                     }
-                    for (int j = 0; j < responseCount; j++) {
-                        final FillResponse response = mResponses.valueAt(j);
-                        final List<Dataset> datasets = response.getDatasets();
-                        if (datasets == null || datasets.isEmpty()) {
-                            if (sVerbose) Slog.v(TAG,  "logContextCommitted() no datasets at " + j);
-                        } else {
-                            for (int k = 0; k < datasets.size(); k++) {
-                                final Dataset dataset = datasets.get(k);
-                                final String datasetId = dataset.getId();
-                                if (datasetId == null) {
-                                    if (sVerbose) {
-                                        Slog.v(TAG, "logContextCommitted() skipping idless dataset "
-                                                + dataset);
-                                    }
-                                } else {
-                                    final ArrayList<AutofillValue> values = dataset.getFieldValues();
-                                    for (int l = 0; l < values.size(); l++) {
-                                        final AutofillValue candidate = values.get(l);
-                                        if (currentValue.equals(candidate)) {
-                                            if (sDebug) {
-                                                Slog.d(TAG, "field " + viewState.id
-                                                        + " was manually filled with value set by "
-                                                        + "dataset " + datasetId);
-                                            }
-                                            if (manuallyFilledIds == null) {
-                                                manuallyFilledIds = new ArrayMap<>();
-                                            }
-                                            ArraySet<String> datasetIds =
-                                                    manuallyFilledIds.get(viewState.id);
-                                            if (datasetIds == null) {
-                                                datasetIds = new ArraySet<>(1);
-                                                manuallyFilledIds.put(viewState.id, datasetIds);
-                                            }
-                                            datasetIds.add(datasetId);
-                                        }
-                                    }
-                                    if (mSelectedDatasetIds == null
-                                            || !mSelectedDatasetIds.contains(datasetId)) {
-                                        if (sVerbose) {
-                                            Slog.v(TAG, "adding ignored dataset " + datasetId);
-                                        }
-                                        if (ignoredDatasets == null) {
-                                            ignoredDatasets = new ArraySet<>();
-                                        }
-                                        ignoredDatasets.add(datasetId);
-                                    }
+                    // Check if value match a dataset.
+                    if (hasAtLeastOneDataset) {
+                        for (int j = 0; j < responseCount; j++) {
+                            final FillResponse response = mResponses.valueAt(j);
+                            final List<Dataset> datasets = response.getDatasets();
+                            if (datasets == null || datasets.isEmpty()) {
+                                if (sVerbose) {
+                                    Slog.v(TAG,  "logContextCommitted() no datasets at " + j);
                                 }
-                            }
-                        }
+                            } else {
+                                for (int k = 0; k < datasets.size(); k++) {
+                                    final Dataset dataset = datasets.get(k);
+                                    final String datasetId = dataset.getId();
+                                    if (datasetId == null) {
+                                        if (sVerbose) {
+                                            Slog.v(TAG, "logContextCommitted() skipping idless "
+                                                    + "dataset " + dataset);
+                                        }
+                                    } else {
+                                        final ArrayList<AutofillValue> values =
+                                                dataset.getFieldValues();
+                                        for (int l = 0; l < values.size(); l++) {
+                                            final AutofillValue candidate = values.get(l);
+                                            if (currentValue.equals(candidate)) {
+                                                if (sDebug) {
+                                                    Slog.d(TAG, "field " + viewState.id + " was "
+                                                            + "manually filled with value set by "
+                                                            + "dataset " + datasetId);
+                                                }
+                                                if (manuallyFilledIds == null) {
+                                                    manuallyFilledIds = new ArrayMap<>();
+                                                }
+                                                ArraySet<String> datasetIds =
+                                                        manuallyFilledIds.get(viewState.id);
+                                                if (datasetIds == null) {
+                                                    datasetIds = new ArraySet<>(1);
+                                                    manuallyFilledIds.put(viewState.id, datasetIds);
+                                                }
+                                                datasetIds.add(datasetId);
+                                            }
+                                        } // for l
+                                        if (mSelectedDatasetIds == null
+                                                || !mSelectedDatasetIds.contains(datasetId)) {
+                                            if (sVerbose) {
+                                                Slog.v(TAG, "adding ignored dataset " + datasetId);
+                                            }
+                                            if (ignoredDatasets == null) {
+                                                ignoredDatasets = new ArraySet<>();
+                                            }
+                                            ignoredDatasets.add(datasetId);
+                                        } // if
+                                    } // if
+                                } // for k
+                            } // else
+                        } // for j
                     }
-                }
-            }
+
+                    // Check if detectable field changed.
+                    if (detectableFieldId != null && detectableFieldId.equals(viewState.id)
+                            && currentValue.isText() && currentValue.getTextValue() != null) {
+                        final String actualValue = currentValue.getTextValue().toString();
+                        final String expectedValue = fieldsDetection.getValue();
+                        if (actualValue.equalsIgnoreCase(expectedValue)) {
+                            detectedRemoteId = detectableRemoteId;
+                            detectedFieldScore = 0;
+                        } else if (sVerbose) {
+                            Slog.v(TAG, "Detection mismatch for field " + detectableFieldId);
+                        }
+                        // TODO(b/67867469): set score on partial hits
+                    }
+                } // else
+            } // else
         }
 
         if (sVerbose) {
@@ -1027,7 +1071,10 @@
                     + ", ignoredDatasetIds=" + ignoredDatasets
                     + ", changedAutofillIds=" + changedFieldIds
                     + ", changedDatasetIds=" + changedDatasetIds
-                    + ", manuallyFilledIds=" + manuallyFilledIds);
+                    + ", manuallyFilledIds=" + manuallyFilledIds
+                    + ", detectableFieldId=" + detectableFieldId
+                    + ", detectedFieldScore=" + detectedFieldScore
+                    );
         }
 
         ArrayList<AutofillId> manuallyFilledFieldIds = null;
@@ -1045,9 +1092,11 @@
                 manuallyFilledDatasetIds.add(new ArrayList<>(datasetIds));
             }
         }
+
         mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
                 changedFieldIds, changedDatasetIds,
-                manuallyFilledFieldIds, manuallyFilledDatasetIds);
+                manuallyFilledFieldIds, manuallyFilledDatasetIds,
+                detectedRemoteId, detectedFieldScore);
     }
 
     /**
@@ -1535,6 +1584,10 @@
                 viewState = new ViewState(this, id, this,
                         isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
                 mViewStates.put(id, viewState);
+
+                // TODO(b/67867469): 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) {
                     if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + id);
                     return;
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 1d8110f..832a66b 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -134,7 +134,11 @@
     }
 
     String getStateAsString() {
-        return DebugUtils.flagsToString(ViewState.class, "STATE_", mState);
+        return getStateAsString(mState);
+    }
+
+    static String getStateAsString(int state) {
+        return DebugUtils.flagsToString(ViewState.class, "STATE_", state);
     }
 
     void setState(int state) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e609bd7..831c9cb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1356,31 +1356,6 @@
         }
     }
 
-    public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
-        if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) {
-            return;
-        }
-
-        synchronized (mRecords) {
-            for (Record r : mRecords) {
-                if (VDBG) {
-                    log("notifyOemHookRawEventForSubscriber:  r=" + r + " subId=" + subId);
-                }
-                if ((r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) &&
-                        ((r.subId == subId) ||
-                        (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
-                    try {
-                        r.callback.onOemHookRawEvent(rawData);
-                    } catch (RemoteException ex) {
-                        mRemoveList.add(r.binder);
-                    }
-                }
-            }
-            handleRemoveListLocked();
-        }
-    }
-
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -1673,11 +1648,6 @@
                     android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
 
         }
-
-        if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
-        }
     }
 
     private void handleRemoveListLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index c04ddf8..ea7b443 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -404,8 +404,8 @@
 
         if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-            return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
-                    windowingMode, activityType);
+            return supportsSplitScreen
+                    && WindowConfiguration.supportSplitScreenWindowingMode(activityType);
         }
 
         if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 842a5b8..02ba6a0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3889,7 +3889,7 @@
             }
 
             if (app.info.isPrivilegedApp() &&
-                    !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+                    SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) {
                 runtimeFlags |= Zygote.DISABLE_VERIFIER;
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
@@ -7075,10 +7075,12 @@
             }
 
             // We deprecated Build.SERIAL and it is not accessible to
-            // apps that target the v2 security sandbox. Since access to
-            // the serial is now behind a permission we push down the value.
-            String buildSerial = appInfo.targetSandboxVersion < 2
-                    ? sTheRealBuildSerial : Build.UNKNOWN;
+            // apps that target the v2 security sandbox and to apps that
+            // target APIs higher than O MR1. Since access to the serial
+            // is now behind a permission we push down the value.
+            final String buildSerial = (appInfo.targetSandboxVersion < 2
+                    && appInfo.targetSdkVersion <= Build.VERSION_CODES.O_MR1)
+                            ? sTheRealBuildSerial : Build.UNKNOWN;
 
             // Check if this is a secondary process that should be incorporated into some
             // currently active instrumentation.  (Note we do this AFTER all of the profiling
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 1db2629..b2308d5 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -201,6 +201,8 @@
     private static final String TAG_STATES = TAG + POSTFIX_STATES;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
     private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+    // TODO(b/67864419): Remove once recents component is overridden
+    private static final String LEGACY_RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
 
     private static final boolean SHOW_ACTIVITY_START_TIME = true;
 
@@ -1057,7 +1059,8 @@
                 // We only allow home activities to be resizeable if they explicitly requested it.
                 info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
             }
-        } else if (service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+        } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME) ||
+                service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
             activityType = ACTIVITY_TYPE_RECENTS;
         } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
                 && canLaunchAssistActivity(launchedFromPackage)) {
@@ -2179,26 +2182,6 @@
         mTmpConfig.unset();
         computeBounds(mTmpBounds);
         if (mTmpBounds.equals(mBounds)) {
-            final ActivityStack stack = getStack();
-            if (!mBounds.isEmpty() || task == null || stack == null || !task.mFullscreen) {
-                // We don't want to influence the override configuration here if our task is in
-                // multi-window mode or there is a bounds specified to calculate the override
-                // config. In both of this cases the app should be compatible with whatever the
-                // current configuration is or will be.
-                return;
-            }
-
-            // Currently limited to the top activity for now to avoid situations where non-top
-            // visible activity and top might have conflicting requests putting the non-top activity
-            // windows in an odd state.
-            final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
-            final Configuration parentConfig = getParent().getConfiguration();
-            if (top != this || isConfigurationCompatible(parentConfig)) {
-                onOverrideConfigurationChanged(mTmpConfig);
-            } else if (isConfigurationCompatible(
-                    mLastReportedConfiguration.getMergedConfiguration())) {
-                onOverrideConfigurationChanged(mLastReportedConfiguration.getMergedConfiguration());
-            }
             return;
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 88403fc..8cc584e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -870,6 +871,29 @@
         }
     }
 
+    /**
+     * @param reason The reason for moving the stack to the back.
+     * @param task If non-null, the task will be moved to the bottom of the stack.
+     **/
+    void moveToBack(String reason, TaskRecord task) {
+        if (!isAttached()) {
+            return;
+        }
+
+        getDisplay().positionChildAtBottom(this);
+        mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack());
+        if (task != null) {
+            insertTaskAtBottom(task);
+            return;
+        } else {
+            task = bottomTask();
+            if (task != null) {
+                mWindowContainerController.positionChildAtBottom(
+                        task.getWindowContainerController(), true /* includingParents */);
+            }
+        }
+    }
+
     boolean isFocusable() {
         if (getWindowConfiguration().canReceiveKeys()) {
             return true;
@@ -1778,6 +1802,15 @@
         return inPinnedWindowingMode();
     }
 
+    /** @return True if the resizing of the primary-split-screen stack affects this stack size. */
+    boolean affectedBySplitScreenResize() {
+        if (!supportsSplitScreenWindowingMode()) {
+            return false;
+        }
+        final int windowingMode = getWindowingMode();
+        return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
+    }
+
     /**
      * @return the top most visible activity that wants to dismiss Keyguard
      */
@@ -2581,6 +2614,9 @@
         if (position >= mTaskHistory.size()) {
             insertTaskAtTop(task, null);
             return;
+        } else if (position <= 0) {
+            insertTaskAtBottom(task);
+            return;
         }
         position = getAdjustedPositionForTask(task, position, null /* starting */);
         mTaskHistory.remove(task);
@@ -2601,6 +2637,16 @@
                 true /* includingParents */);
     }
 
+    private void insertTaskAtBottom(TaskRecord task) {
+        // Unlike insertTaskAtPosition, this will also position parents of the windowcontroller.
+        mTaskHistory.remove(task);
+        final int position = getAdjustedPositionForTask(task, 0, null);
+        mTaskHistory.add(position, task);
+        updateTaskMovement(task, true);
+        mWindowContainerController.positionChildAtBottom(task.getWindowContainerController(),
+                true /* includingParents */);
+    }
+
     final void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
             boolean newTask, boolean keepCurTransition, ActivityOptions options) {
         TaskRecord rTask = r.getTask();
@@ -4370,8 +4416,7 @@
         updateTaskMovement(tr, false);
 
         mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
-        mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController(),
-                true /* includingParents */);
+        moveToBack("moveTaskToBackLocked", tr);
 
         if (inPinnedWindowingMode()) {
             mStackSupervisor.removeStack(this);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index a0aeb63..062083c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2441,7 +2441,7 @@
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
         mWindowManager.deferSurfaceLayout();
         try {
-            if (stack.supportsSplitScreenWindowingMode()) {
+            if (stack.affectedBySplitScreenResize()) {
                 if (bounds == null && stack.inSplitScreenWindowingMode()) {
                     // null bounds = fullscreen windowing mode...at least for now.
                     stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -2541,8 +2541,6 @@
                                 null, mTmpOptions, task, task.getActivityType(), onTop);
 
                     if (onTop) {
-                        final int returnToType =
-                                toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
                         final boolean isTopTask = i == (size - 1);
                         // Defer resume until all the tasks have been moved to the fullscreen stack
                         task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
@@ -2631,7 +2629,7 @@
                     if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                         continue;
                     }
-                    if (!current.supportsSplitScreenWindowingMode()) {
+                    if (!current.affectedBySplitScreenResize()) {
                         continue;
                     }
                     // Need to set windowing mode here before we try to get the dock bounds.
@@ -4177,8 +4175,8 @@
         }
 
         // Handle incorrect launch/move to secondary display if needed.
-        final boolean launchOnSecondaryDisplayFailed;
         if (isSecondaryDisplayPreferred) {
+            final boolean launchOnSecondaryDisplayFailed;
             final int actualDisplayId = task.getStack().mDisplayId;
             if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
                 // The task landed on an inappropriate display somehow, move it to the default
@@ -4193,34 +4191,34 @@
                         || (preferredDisplayId != INVALID_DISPLAY
                             && preferredDisplayId != actualDisplayId);
             }
-        } else {
-            // The task wasn't requested to be on a secondary display.
-            launchOnSecondaryDisplayFailed = false;
-        }
-
-        final ActivityRecord topActivity = task.getTopActivity();
-        if (launchOnSecondaryDisplayFailed
-                || !task.supportsSplitScreenWindowingMode() || forceNonResizable) {
             if (launchOnSecondaryDisplayFailed) {
                 // Display a warning toast that we tried to put a non-resizeable task on a secondary
                 // display with config different from global config.
                 mService.mTaskChangeNotificationController
                         .notifyActivityLaunchOnSecondaryDisplayFailed();
-            } else {
-                // Display a warning toast that we tried to put a non-dockable task in the docked
-                // stack.
-                mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+                return;
             }
+        }
+
+        final ActivityRecord topActivity = task.getTopActivity();
+        if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
+            // Display a warning toast that we tried to put a non-dockable task in the docked
+            // stack.
+            mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
 
             // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
             // we need to move it to top of fullscreen stack, otherwise it will be covered.
 
-            final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenPrimaryStack();
+            final ActivityStack dockedStack =
+                    task.getStack().getDisplay().getSplitScreenPrimaryStack();
             if (dockedStack != null) {
                 moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
             }
-        } else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
-                && !topActivity.noDisplay) {
+            return;
+        }
+
+        if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
+            && !topActivity.noDisplay) {
             final String packageName = topActivity.appInfo.packageName;
             final int reason = isSecondaryDisplayPreferred
                     ? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
@@ -4489,9 +4487,12 @@
                         "startActivityFromRecents: Task " + taskId + " not found.");
             }
 
-            // We always want to return to the home activity instead of the recents activity from
-            // whatever is started from the recents activity, so move the home stack forward.
-            moveHomeStackToFront("startActivityFromRecents");
+            if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                // We always want to return to the home activity instead of the recents activity
+                // from whatever is started from the recents activity, so move the home stack
+                // forward.
+                moveHomeStackToFront("startActivityFromRecents");
+            }
 
             // If the user must confirm credentials (e.g. when first launching a work app and the
             // Work Challenge is present) let startActivityInPackage handle the intercepting.
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index c3fed17..76b4679 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -41,10 +41,8 @@
 import android.os.Trace;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.server.wm.WindowManagerService;
-
 import java.io.PrintWriter;
 
 /**
@@ -236,24 +234,33 @@
         final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
         mOccluded = false;
         mDismissingKeyguardActivity = null;
-        final ActivityDisplay display = mStackSupervisor.getDefaultDisplay();
-        for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = display.getChildAt(stackNdx);
 
-            // Only the focused stack top activity may control occluded state
-            if (mStackSupervisor.isFocusedStack(stack)) {
+        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
 
-                // A dismissing activity occludes Keyguard in the insecure case for legacy reasons.
-                final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
-                mOccluded = stack.topActivityOccludesKeyguard()
-                        || (topDismissing != null
-                                && stack.topRunningActivityLocked() == topDismissing
-                                && canShowWhileOccluded(true /* dismissKeyguard */,
-                                        false /* showWhenLocked */));
-            }
-            if (mDismissingKeyguardActivity == null
-                    && stack.getTopDismissingKeyguardActivity() != null) {
-                mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
+                // Only the top activity of the focused stack on the default display may control
+                // occluded state.
+                if (display.mDisplayId == DEFAULT_DISPLAY
+                        && mStackSupervisor.isFocusedStack(stack)) {
+
+                    // A dismissing activity occludes Keyguard in the insecure case for legacy
+                    // reasons.
+                    final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
+                    mOccluded =
+                            stack.topActivityOccludesKeyguard()
+                                    || (topDismissing != null
+                                            && stack.topRunningActivityLocked() == topDismissing
+                                            && canShowWhileOccluded(
+                                                    true /* dismissKeyguard */,
+                                                    false /* showWhenLocked */));
+                }
+
+                if (mDismissingKeyguardActivity == null
+                        && stack.getTopDismissingKeyguardActivity() != null) {
+                    mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
+                }
             }
         }
         mOccluded |= mWindowManager.isShowingDream();
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c5514fb..40b1cac 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1139,6 +1139,9 @@
 
         mActivities.remove(newTop);
         mActivities.add(newTop);
+
+        // Make sure window manager is aware of the position change.
+        mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
         updateEffectiveIntent();
 
         setFrontOfTask();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3f23737..15a418dc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1345,8 +1345,9 @@
         } else {
             final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
             final boolean activeForReal;
-            if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) {
-                activeForReal = isAfMusicActiveRecently(0);
+            if (maybeActiveStreamType == AudioSystem.STREAM_RING
+                    || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
+                activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
             } else {
                 activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
             }
@@ -3883,13 +3884,13 @@
 
     /**
      * For code clarity for getActiveStreamType(int)
-     * @param delay_ms max time since last STREAM_MUSIC activity to consider
-     * @return true if STREAM_MUSIC is active in streams handled by AudioFlinger now or
+     * @param delay_ms max time since last stream activity to consider
+     * @return true if stream is active in streams handled by AudioFlinger now or
      *     in the last "delay_ms" ms.
      */
-    private boolean isAfMusicActiveRecently(int delay_ms) {
-        return AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, delay_ms)
-                || AudioSystem.isStreamActiveRemotely(AudioSystem.STREAM_MUSIC, delay_ms);
+    private boolean wasStreamActiveRecently(int stream, int delay_ms) {
+        return AudioSystem.isStreamActive(stream, delay_ms)
+                || AudioSystem.isStreamActiveRemotely(stream, delay_ms);
     }
 
     private int getActiveStreamType(int suggestedStreamType) {
@@ -3910,21 +3911,30 @@
                     return AudioSystem.STREAM_VOICE_CALL;
                 }
             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-                if (isAfMusicActiveRecently(sStreamOverrideDelayMs)) {
+                if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
                     if (DEBUG_VOL)
-                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
+                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
+                    return AudioSystem.STREAM_RING;
+                } else if (wasStreamActiveRecently(
+                        AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
+                    if (DEBUG_VOL)
+                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION stream active");
+                    return AudioSystem.STREAM_NOTIFICATION;
+                } else {
+                    if (DEBUG_VOL)
+                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC b/c default");
                     return AudioSystem.STREAM_MUSIC;
-                    } else {
-                        if (DEBUG_VOL)
-                            Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
-                        return AudioSystem.STREAM_RING;
                 }
-            } else if (isAfMusicActiveRecently(0)) {
+            } else if (
+                    wasStreamActiveRecently(AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
                 if (DEBUG_VOL)
-                    Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
-                return AudioSystem.STREAM_MUSIC;
+                    Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION stream active");
+                return AudioSystem.STREAM_NOTIFICATION;
+            } else if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+                if (DEBUG_VOL)
+                    Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
+                return AudioSystem.STREAM_RING;
             }
-            break;
         default:
             if (isInCommunication()) {
                 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
@@ -3935,20 +3945,26 @@
                     if (DEBUG_VOL)  Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
                     return AudioSystem.STREAM_VOICE_CALL;
                 }
-            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
-                    sStreamOverrideDelayMs) ||
-                    AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
-                            sStreamOverrideDelayMs)) {
+            } else if (AudioSystem.isStreamActive(
+                    AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
                 return AudioSystem.STREAM_NOTIFICATION;
+            } else if (AudioSystem.isStreamActive(
+                    AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+                if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
+                return AudioSystem.STREAM_RING;
             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-                if (isAfMusicActiveRecently(sStreamOverrideDelayMs)) {
-                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
-                    return AudioSystem.STREAM_MUSIC;
-                } else {
-                    if (DEBUG_VOL) Log.v(TAG,
-                            "getActiveStreamType: using STREAM_NOTIFICATION as default");
+                if (AudioSystem.isStreamActive(
+                        AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
+                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
                     return AudioSystem.STREAM_NOTIFICATION;
+                } else if (AudioSystem.isStreamActive(
+                        AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
+                    return AudioSystem.STREAM_RING;
+                } else {
+                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
+                    return AudioSystem.STREAM_MUSIC;
                 }
             }
             break;
@@ -6349,10 +6365,10 @@
     //   stream override timeout when adjusting volume
     //---------------------------------------------------------------------------------
 
-    // AudioService.getActiveStreamType() will return:
     // - STREAM_NOTIFICATION on tablets during this period after a notification stopped
-    // - STREAM_MUSIC on phones during this period after music or talkback/voice search prompt
-    // stopped
+    // - STREAM_RING on phones during this period after a notification stopped
+    // - STREAM_MUSIC otherwise
+
     private static final int DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS = 0;
     private static final int TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS = 1000;
 
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index a011692b..397af7b 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -46,7 +46,6 @@
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
-import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId;
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -128,6 +127,11 @@
         wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups;
         wakeupStats.applicationWakeups = in.applicationWakeups;
         wakeupStats.noUidWakeups = in.noUidWakeups;
+        wakeupStats.l2UnicastCount = in.l2UnicastCount;
+        wakeupStats.l2MulticastCount = in.l2MulticastCount;
+        wakeupStats.l2BroadcastCount = in.l2BroadcastCount;
+        wakeupStats.ethertypeCounts = toPairArray(in.ethertypes);
+        wakeupStats.ipNextHeaderCounts = toPairArray(in.ipNextHeaders);
         final IpConnectivityEvent out = buildEvent(0, 0, in.iface);
         out.setWakeupStats(wakeupStats);
         return out;
@@ -242,7 +246,6 @@
     private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) {
         IpConnectivityLogClass.NetworkEvent networkEvent =
                 new IpConnectivityLogClass.NetworkEvent();
-        networkEvent.networkId = netIdOf(in.netId);
         networkEvent.eventType = in.eventType;
         networkEvent.latencyMs = (int) in.durationMs;
         out.setNetworkEvent(networkEvent);
@@ -321,12 +324,6 @@
         return pairs;
     }
 
-    private static NetworkId netIdOf(int netid) {
-        final NetworkId ni = new NetworkId();
-        ni.networkId = netid;
-        return ni;
-    }
-
     private static int ipSupportOf(DefaultNetworkEvent in) {
         if (in.ipv4 && in.ipv6) {
             return IpConnectivityLogClass.DefaultNetworkEvent.DUAL;
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 5cc390a..f427819 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -23,8 +23,6 @@
 import android.net.metrics.ApfProgramEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcelable;
 import android.os.Process;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -322,22 +320,22 @@
         }
 
         @Override
-        public boolean registerNetdEventCallback(INetdEventCallback callback) {
+        public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
             enforceNetdEventListeningPermission();
             if (mNetdListener == null) {
                 return false;
             }
-            return mNetdListener.registerNetdEventCallback(callback);
+            return mNetdListener.addNetdEventCallback(callerType, callback);
         }
 
         @Override
-        public boolean unregisterNetdEventCallback() {
+        public boolean removeNetdEventCallback(int callerType) {
             enforceNetdEventListeningPermission();
             if (mNetdListener == null) {
                 // if the service is null, we aren't registered anyway
                 return true;
             }
-            return mNetdListener.unregisterNetdEventCallback();
+            return mNetdListener.removeNetdEventCallback(callerType);
         }
     };
 
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 61b11e1..4bdbbe3 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -58,7 +58,6 @@
 
     private static final String TAG = NetdEventListenerService.class.getSimpleName();
     private static final boolean DBG = false;
-    private static final boolean VDBG = false;
 
     // Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
     // bursts of 5000 measurements.
@@ -98,21 +97,55 @@
     @GuardedBy("this")
     private final TokenBucket mConnectTb =
             new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT);
-    // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM
-    // by the device owner. It's DevicePolicyManager's responsibility to ensure that.
-    @GuardedBy("this")
-    private INetdEventCallback mNetdEventCallback;
 
-    public synchronized boolean registerNetdEventCallback(INetdEventCallback callback) {
-        mNetdEventCallback = callback;
+
+    /**
+     * There are only 2 possible callbacks.
+     *
+     * mNetdEventCallbackList[CALLBACK_CALLER_DEVICE_POLICY].
+     * Callback registered/unregistered when logging is being enabled/disabled in DPM
+     * by the device owner. It's DevicePolicyManager's responsibility to ensure that.
+     *
+     * mNetdEventCallbackList[CALLBACK_CALLER_NETWORK_WATCHLIST]
+     * Callback registered/unregistered by NetworkWatchlistService.
+     */
+    @GuardedBy("this")
+    private static final int[] ALLOWED_CALLBACK_TYPES = {
+        INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY,
+        INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST
+    };
+
+    @GuardedBy("this")
+    private INetdEventCallback[] mNetdEventCallbackList =
+            new INetdEventCallback[ALLOWED_CALLBACK_TYPES.length];
+
+    public synchronized boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+        if (!isValidCallerType(callerType)) {
+            Log.e(TAG, "Invalid caller type: " + callerType);
+            return false;
+        }
+        mNetdEventCallbackList[callerType] = callback;
         return true;
     }
 
-    public synchronized boolean unregisterNetdEventCallback() {
-        mNetdEventCallback = null;
+    public synchronized boolean removeNetdEventCallback(int callerType) {
+        if (!isValidCallerType(callerType)) {
+            Log.e(TAG, "Invalid caller type: " + callerType);
+            return false;
+        }
+        mNetdEventCallbackList[callerType] = null;
         return true;
     }
 
+    private static boolean isValidCallerType(int callerType) {
+        for (int i = 0; i < ALLOWED_CALLBACK_TYPES.length; i++) {
+            if (callerType == ALLOWED_CALLBACK_TYPES[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public NetdEventListenerService(Context context) {
         this(context.getSystemService(ConnectivityManager.class));
     }
@@ -164,13 +197,13 @@
     public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
             String hostname, String[] ipAddresses, int ipAddressesCount, int uid)
             throws RemoteException {
-        maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs);
-
         long timestamp = System.currentTimeMillis();
         getMetricsForNetwork(timestamp, netId).addDnsResult(eventType, returnCode, latencyMs);
 
-        if (mNetdEventCallback != null) {
-            mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
+        for (INetdEventCallback callback : mNetdEventCallbackList) {
+            if (callback != null) {
+                callback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
+            }
         }
     }
 
@@ -179,22 +212,23 @@
     // This method must not block or perform long-running operations.
     public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
             int port, int uid) throws RemoteException {
-        maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
-
         long timestamp = System.currentTimeMillis();
         getMetricsForNetwork(timestamp, netId).addConnectResult(error, latencyMs, ipAddr);
 
-        if (mNetdEventCallback != null) {
-            mNetdEventCallback.onConnectEvent(ipAddr, port, timestamp, uid);
+        for (INetdEventCallback callback : mNetdEventCallbackList) {
+            if (callback != null) {
+                // TODO(rickywai): Remove this checking to collect ip in watchlist.
+                if (callback ==
+                        mNetdEventCallbackList[INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY]) {
+                    callback.onConnectEvent(ipAddr, port, timestamp, uid);
+                }
+            }
         }
     }
 
     @Override
-    public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
-        maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
-
-        // TODO: add ip protocol and port
-
+    public synchronized void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader,
+            byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs) {
         String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
         final long timestampMs;
         if (timestampNs > 0) {
@@ -203,15 +237,22 @@
             timestampMs = System.currentTimeMillis();
         }
 
-        addWakeupEvent(iface, timestampMs, uid);
-    }
-
-    @GuardedBy("this")
-    private void addWakeupEvent(String iface, long timestampMs, int uid) {
         WakeupEvent event = new WakeupEvent();
         event.iface = iface;
         event.timestampMs = timestampMs;
         event.uid = uid;
+        event.ethertype = ethertype;
+        event.dstHwAddr = dstHw;
+        event.srcIp = srcIp;
+        event.dstIp = dstIp;
+        event.ipNextHeader = ipNextHeader;
+        event.srcPort = srcPort;
+        event.dstPort = dstPort;
+        addWakeupEvent(event);
+    }
+
+    private void addWakeupEvent(WakeupEvent event) {
+        String iface = event.iface;
         mWakeupEvents.append(event);
         WakeupStats stats = mWakeupStats.get(iface);
         if (stats == null) {
@@ -291,10 +332,6 @@
         if (DBG) Log.d(TAG, String.format(s, args));
     }
 
-    private static void maybeVerboseLog(String s, Object... args) {
-        if (VDBG) Log.d(TAG, String.format(s, args));
-    }
-
     /** Helper class for buffering summaries of NetworkMetrics at regular time intervals */
     static class NetworkMetricsSnapshot {
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 8b886d6..7684030 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -1129,7 +1129,8 @@
     }
 
     private void logNetworkEvent(int evtype) {
-        mMetricsLog.log(new NetworkEvent(mNetId, evtype));
+        int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+        mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype));
     }
 
     private int networkEventType(ValidationStage s, EvaluationResult r) {
@@ -1150,7 +1151,8 @@
 
     private void maybeLogEvaluationResult(int evtype) {
         if (mEvaluationTimer.isRunning()) {
-            mMetricsLog.log(new NetworkEvent(mNetId, evtype, mEvaluationTimer.stop()));
+            int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+            mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype, mEvaluationTimer.stop()));
             mEvaluationTimer.reset();
         }
     }
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
similarity index 97%
rename from services/core/java/com/android/server/display/NightDisplayService.java
rename to services/core/java/com/android/server/display/ColorDisplayService.java
index a7c3ff9..af8ecad 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -42,7 +42,7 @@
 import android.util.Slog;
 import android.view.animation.AnimationUtils;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.server.SystemService;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
@@ -60,10 +60,10 @@
 /**
  * Tints the display at night.
  */
-public final class NightDisplayService extends SystemService
-        implements NightDisplayController.Callback {
+public final class ColorDisplayService extends SystemService
+        implements ColorDisplayController.Callback {
 
-    private static final String TAG = "NightDisplayService";
+    private static final String TAG = "ColorDisplayService";
 
     /**
      * The transition time, in milliseconds, for Night Display to turn on/off.
@@ -119,12 +119,12 @@
     private ContentObserver mUserSetupObserver;
     private boolean mBootCompleted;
 
-    private NightDisplayController mController;
+    private ColorDisplayController mController;
     private ValueAnimator mColorMatrixAnimator;
     private Boolean mIsActivated;
     private AutoMode mAutoMode;
 
-    public NightDisplayService(Context context) {
+    public ColorDisplayService(Context context) {
         super(context);
         mHandler = new Handler(Looper.getMainLooper());
     }
@@ -228,7 +228,7 @@
         Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
 
         // Create a new controller for the current user and start listening for changes.
-        mController = new NightDisplayController(getContext(), mCurrentUser);
+        mController = new ColorDisplayController(getContext(), mCurrentUser);
         mController.setListener(this);
 
         setCoefficientMatrix(getContext());
@@ -293,9 +293,9 @@
             mAutoMode = null;
         }
 
-        if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM) {
+        if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
             mAutoMode = new CustomAutoMode();
-        } else if (autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
+        } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
             mAutoMode = new TwilightAutoMode();
         }
 
@@ -463,7 +463,7 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
-    private abstract class AutoMode implements NightDisplayController.Callback {
+    private abstract class AutoMode implements ColorDisplayController.Callback {
         public abstract void onStart();
 
         public abstract void onStop();
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index bef6898..338e331 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -29,7 +29,7 @@
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import java.util.Arrays;
 
 /**
@@ -223,13 +223,13 @@
     }
 
     public boolean setColorMode(int colorMode) {
-        if (colorMode == NightDisplayController.COLOR_MODE_NATURAL) {
+        if (colorMode == ColorDisplayController.COLOR_MODE_NATURAL) {
             applySaturation(COLOR_SATURATION_NATURAL);
             setNativeMode(false);
-        } else if (colorMode == NightDisplayController.COLOR_MODE_BOOSTED) {
+        } else if (colorMode == ColorDisplayController.COLOR_MODE_BOOSTED) {
             applySaturation(COLOR_SATURATION_BOOSTED);
             setNativeMode(false);
-        } else if (colorMode == NightDisplayController.COLOR_MODE_SATURATED) {
+        } else if (colorMode == ColorDisplayController.COLOR_MODE_SATURATED) {
             applySaturation(COLOR_SATURATION_NATURAL);
             setNativeMode(true);
         }
diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java
index 025ff0b..296743b 100644
--- a/services/core/java/com/android/server/job/JobPackageTracker.java
+++ b/services/core/java/com/android/server/job/JobPackageTracker.java
@@ -16,15 +16,19 @@
 
 package com.android.server.job;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.JobSchedulerService.sSystemClock;
+import static com.android.server.job.JobSchedulerService.sUptimeMillisClock;
+
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
+
 import com.android.internal.util.RingBufferIndices;
 import com.android.server.job.controllers.JobStatus;
 
@@ -57,7 +61,7 @@
     public void addEvent(int cmd, int uid, String tag, int jobId, int stopReason) {
         int index = mEventIndices.add();
         mEventCmds[index] = cmd | ((stopReason<<EVENT_STOP_REASON_SHIFT) & EVENT_STOP_REASON_MASK);
-        mEventTimes[index] = SystemClock.elapsedRealtime();
+        mEventTimes[index] = sElapsedRealtimeClock.millis();
         mEventUids[index] = uid;
         mEventTags[index] = tag;
         mEventJobIds[index] = jobId;
@@ -125,9 +129,9 @@
         }
 
         public DataSet() {
-            mStartUptimeTime = SystemClock.uptimeMillis();
-            mStartElapsedTime = SystemClock.elapsedRealtime();
-            mStartClockTime = System.currentTimeMillis();
+            mStartUptimeTime = sUptimeMillisClock.millis();
+            mStartElapsedTime = sElapsedRealtimeClock.millis();
+            mStartClockTime = sSystemClock.millis();
         }
 
         private PackageEntry getOrCreateEntry(int uid, String pkg) {
@@ -376,20 +380,20 @@
     }
 
     public void notePending(JobStatus job) {
-        final long now = SystemClock.uptimeMillis();
+        final long now = sUptimeMillisClock.millis();
         job.madePending = now;
         rebatchIfNeeded(now);
         mCurDataSet.incPending(job.getSourceUid(), job.getSourcePackageName(), now);
     }
 
     public void noteNonpending(JobStatus job) {
-        final long now = SystemClock.uptimeMillis();
+        final long now = sUptimeMillisClock.millis();
         mCurDataSet.decPending(job.getSourceUid(), job.getSourcePackageName(), now);
         rebatchIfNeeded(now);
     }
 
     public void noteActive(JobStatus job) {
-        final long now = SystemClock.uptimeMillis();
+        final long now = sUptimeMillisClock.millis();
         job.madeActive = now;
         rebatchIfNeeded(now);
         if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
@@ -402,7 +406,7 @@
     }
 
     public void noteInactive(JobStatus job, int stopReason) {
-        final long now = SystemClock.uptimeMillis();
+        final long now = sUptimeMillisClock.millis();
         if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
             mCurDataSet.decActiveTop(job.getSourceUid(), job.getSourcePackageName(), now,
                     stopReason);
@@ -431,7 +435,7 @@
         if (cur == null && last == null) {
             return 0;
         }
-        final long now = SystemClock.uptimeMillis();
+        final long now = sUptimeMillisClock.millis();
         long time = 0;
         if (cur != null) {
             time += cur.getActiveTime(now) + cur.getPendingTime(now);
@@ -445,8 +449,8 @@
     }
 
     public void dump(PrintWriter pw, String prefix, int filterUid) {
-        final long now = SystemClock.uptimeMillis();
-        final long nowEllapsed = SystemClock.elapsedRealtime();
+        final long now = sUptimeMillisClock.millis();
+        final long nowEllapsed = sElapsedRealtimeClock.millis();
         final DataSet total;
         if (mLastDataSets[0] != null) {
             total = new DataSet(mLastDataSets[0]);
@@ -470,7 +474,7 @@
             return false;
         }
         pw.println("  Job history:");
-        final long now = SystemClock.elapsedRealtime();
+        final long now = sElapsedRealtimeClock.millis();
         for (int i=0; i<size; i++) {
             final int index = mEventIndices.indexOf(i);
             final int uid = mEventUids[index];
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 78aa2f9..b549768 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -19,24 +19,15 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.IUidObserver;
+import android.app.job.IJobScheduler;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
-import android.app.job.IJobScheduler;
 import android.app.job.JobWorkItem;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -46,8 +37,8 @@
 import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.BatteryStats;
@@ -55,8 +46,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Process;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -71,6 +62,7 @@
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.util.ArrayUtils;
@@ -93,6 +85,16 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
 /**
  * Responsible for taking jobs representing work to be performed by a client app, and determining
  * based on the criteria specified when that job should be run against the client application's
@@ -117,6 +119,12 @@
     /** The maximum number of jobs that we allow an unprivileged app to schedule */
     private static final int MAX_JOBS_PER_APP = 100;
 
+    @VisibleForTesting
+    public static Clock sSystemClock = Clock.systemUTC();
+    @VisibleForTesting
+    public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
+    @VisibleForTesting
+    public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
 
     /** Global local for all job scheduler state. */
     final Object mLock = new Object();
@@ -960,7 +968,7 @@
             if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
                 // When we reach clock sanity, recalculate the temporal windows
                 // of all affected jobs.
-                if (mJobs.clockNowValidToInflate(System.currentTimeMillis())) {
+                if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
                     Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
 
                     // We've done our job now, so stop watching the time.
@@ -1068,7 +1076,7 @@
         if (!jobStatus.isPreparedLocked()) {
             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
         }
-        jobStatus.enqueueTime = SystemClock.elapsedRealtime();
+        jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
         final boolean update = mJobs.add(jobStatus);
         if (mReadyToRock) {
             for (int i = 0; i < mControllers.size(); i++) {
@@ -1156,7 +1164,7 @@
      * @see #maybeQueueReadyJobsForExecutionLocked
      */
     private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
-        final long elapsedNowMillis = SystemClock.elapsedRealtime();
+        final long elapsedNowMillis = sElapsedRealtimeClock.millis();
         final JobInfo job = failureToReschedule.getJob();
 
         final long initialBackoffMillis = job.getInitialBackoffMillis();
@@ -1200,7 +1208,7 @@
                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
         JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
-                failureToReschedule.getLastSuccessfulRunTime(), System.currentTimeMillis());
+                failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
         for (int ic=0; ic<mControllers.size(); ic++) {
             StateController controller = mControllers.get(ic);
             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
@@ -1220,7 +1228,7 @@
      * recurring job.
      */
     private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
-        final long elapsedNow = SystemClock.elapsedRealtime();
+        final long elapsedNow = sElapsedRealtimeClock.millis();
         // Compute how much of the period is remaining.
         long runEarly = 0L;
 
@@ -1239,7 +1247,7 @@
         }
         return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
                 newLatestRuntimeElapsed, 0 /* backoffAttempt */,
-                System.currentTimeMillis() /* lastSuccessfulRunTime */,
+                sSystemClock.millis() /* lastSuccessfulRunTime */,
                 periodicToReschedule.getLastFailedRunTime());
     }
 
@@ -2367,8 +2375,8 @@
         }
 
         final int filterUidFinal = UserHandle.getAppId(filterUid);
-        final long nowElapsed = SystemClock.elapsedRealtime();
-        final long nowUptime = SystemClock.uptimeMillis();
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        final long nowUptime = sUptimeMillisClock.millis();
         synchronized (mLock) {
             mConstants.dump(pw);
             pw.println();
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index a7e674b..ac7297e 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -16,11 +16,13 @@
 
 package com.android.server.job;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
 import android.app.ActivityManager;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
 import android.app.job.IJobCallback;
 import android.app.job.IJobService;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
 import android.app.job.JobWorkItem;
 import android.content.ComponentName;
 import android.content.Context;
@@ -34,7 +36,6 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.util.Slog;
@@ -202,7 +203,7 @@
             mRunningCallback = new JobCallback();
             final boolean isDeadlineExpired =
                     job.hasDeadlineConstraint() &&
-                            (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
+                            (job.getLatestRunTimeElapsed() < sElapsedRealtimeClock.millis());
             Uri[] triggeredUris = null;
             if (job.changedUris != null) {
                 triggeredUris = new Uri[job.changedUris.size()];
@@ -217,7 +218,7 @@
             mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
                     ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
                     isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network);
-            mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
+            mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();
 
             // Once we'e begun executing a job, we by definition no longer care whether
             // it was inflated from disk with not-yet-coherent delay/deadline bounds.
@@ -428,7 +429,7 @@
             sb.append("Caller no longer running");
             if (cb.mStoppedReason != null) {
                 sb.append(", last stopped ");
-                TimeUtils.formatDuration(SystemClock.elapsedRealtime() - cb.mStoppedTime, sb);
+                TimeUtils.formatDuration(sElapsedRealtimeClock.millis() - cb.mStoppedTime, sb);
                 sb.append(" because: ");
                 sb.append(cb.mStoppedReason);
             }
@@ -457,7 +458,7 @@
                             sb.append("Ignoring timeout of no longer active job");
                             if (jc.mStoppedReason != null) {
                                 sb.append(", stopped ");
-                                TimeUtils.formatDuration(SystemClock.elapsedRealtime()
+                                TimeUtils.formatDuration(sElapsedRealtimeClock.millis()
                                         - jc.mStoppedTime, sb);
                                 sb.append(" because: ");
                                 sb.append(jc.mStoppedReason);
@@ -740,7 +741,7 @@
     private void applyStoppedReasonLocked(String reason) {
         if (reason != null && mStoppedReason == null) {
             mStoppedReason = reason;
-            mStoppedTime = SystemClock.elapsedRealtime();
+            mStoppedTime = sElapsedRealtimeClock.millis();
             if (mRunningCallback != null) {
                 mRunningCallback.mStoppedReason = mStoppedReason;
                 mRunningCallback.mStoppedTime = mStoppedTime;
@@ -777,7 +778,7 @@
         }
         Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
         mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
-        mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
+        mTimeoutElapsed = sElapsedRealtimeClock.millis() + timeoutMillis;
     }
 
 
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index aa9f77c..1af3b39 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -16,20 +16,23 @@
 
 package com.android.server.job;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.JobSchedulerService.sSystemClock;
+
 import android.app.ActivityManager;
 import android.app.IActivityManager;
-import android.content.ComponentName;
 import android.app.job.JobInfo;
+import android.content.ComponentName;
 import android.content.Context;
+import android.net.NetworkRequest;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.PersistableBundle;
 import android.os.Process;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateUtils;
-import android.util.AtomicFile;
 import android.util.ArraySet;
+import android.util.AtomicFile;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -37,11 +40,16 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.BitUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.IoThread;
 import com.android.server.job.JobSchedulerInternal.JobStorePersistStats;
 import com.android.server.job.controllers.JobStatus;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -53,10 +61,6 @@
 import java.util.List;
 import java.util.Set;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 /**
  * Maintains the master list of jobs that the job scheduler is tracking. These jobs are compared by
  * reference, so none of the functions in this class should make a copy.
@@ -141,7 +145,7 @@
         // a *correct* timestamp, see a bunch of overdue jobs, and run them; then
         // settle into normal operation.
         mXmlTimestamp = mJobsFile.getLastModifiedTime();
-        mRtcGood = (System.currentTimeMillis() > mXmlTimestamp);
+        mRtcGood = (sSystemClock.millis() > mXmlTimestamp);
 
         readJobMapFromDisk(mJobSet, mRtcGood);
     }
@@ -161,7 +165,7 @@
      */
     public void getRtcCorrectedJobsLocked(final ArrayList<JobStatus> toAdd,
             final ArrayList<JobStatus> toRemove) {
-        final long elapsedNow = SystemClock.elapsedRealtime();
+        final long elapsedNow = sElapsedRealtimeClock.millis();
 
         // Find the jobs that need to be fixed up, collecting them for post-iteration
         // replacement with their new versions
@@ -323,7 +327,7 @@
     private final Runnable mWriteRunnable = new Runnable() {
         @Override
         public void run() {
-            final long startElapsed = SystemClock.elapsedRealtime();
+            final long startElapsed = sElapsedRealtimeClock.millis();
             final List<JobStatus> storeCopy = new ArrayList<JobStatus>();
             synchronized (mLock) {
                 // Clone the jobs so we can release the lock before writing.
@@ -338,7 +342,7 @@
             }
             writeJobsMapImpl(storeCopy);
             if (DEBUG) {
-                Slog.v(TAG, "Finished writing, took " + (SystemClock.elapsedRealtime()
+                Slog.v(TAG, "Finished writing, took " + (sElapsedRealtimeClock.millis()
                         - startElapsed) + "ms");
             }
         }
@@ -454,17 +458,12 @@
          */
         private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException {
             out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS);
-            if (jobStatus.needsAnyConnectivity()) {
-                out.attribute(null, "connectivity", Boolean.toString(true));
-            }
-            if (jobStatus.needsMeteredConnectivity()) {
-                out.attribute(null, "metered", Boolean.toString(true));
-            }
-            if (jobStatus.needsUnmeteredConnectivity()) {
-                out.attribute(null, "unmetered", Boolean.toString(true));
-            }
-            if (jobStatus.needsNonRoamingConnectivity()) {
-                out.attribute(null, "not-roaming", Boolean.toString(true));
+            if (jobStatus.hasConnectivityConstraint()) {
+                final NetworkRequest network = jobStatus.getJob().getRequiredNetwork();
+                out.attribute(null, "net-capabilities", Long.toString(
+                        BitUtils.packBits(network.networkCapabilities.getCapabilities())));
+                out.attribute(null, "net-transport-types", Long.toString(
+                        BitUtils.packBits(network.networkCapabilities.getTransportTypes())));
             }
             if (jobStatus.hasIdleConstraint()) {
                 out.attribute(null, "idle", Boolean.toString(true));
@@ -497,8 +496,8 @@
                 Slog.i(TAG, "storing original UTC timestamps for " + jobStatus);
             }
 
-            final long nowRTC = System.currentTimeMillis();
-            final long nowElapsed = SystemClock.elapsedRealtime();
+            final long nowRTC = sSystemClock.millis();
+            final long nowElapsed = sElapsedRealtimeClock.millis();
             if (jobStatus.hasDeadlineConstraint()) {
                 // Wall clock deadline.
                 final long deadlineWallclock = (utcJobTimes == null)
@@ -538,7 +537,7 @@
      */
     private static Pair<Long, Long> convertRtcBoundsToElapsed(Pair<Long, Long> rtcTimes,
             long nowElapsed) {
-        final long nowWallclock = System.currentTimeMillis();
+        final long nowWallclock = sSystemClock.millis();
         final long earliest = (rtcTimes.first > JobStatus.NO_EARLIEST_RUNTIME)
                 ? nowElapsed + Math.max(rtcTimes.first - nowWallclock, 0)
                 : JobStatus.NO_EARLIEST_RUNTIME;
@@ -581,7 +580,7 @@
                 synchronized (mLock) {
                     jobs = readJobMapImpl(fis, rtcGood);
                     if (jobs != null) {
-                        long now = SystemClock.elapsedRealtime();
+                        long now = sElapsedRealtimeClock.millis();
                         IActivityManager am = ActivityManager.getService();
                         for (int i=0; i<jobs.size(); i++) {
                             JobStatus js = jobs.get(i);
@@ -754,7 +753,7 @@
                 return null;
             }
 
-            final long elapsedNow = SystemClock.elapsedRealtime();
+            final long elapsedNow = sElapsedRealtimeClock.millis();
             Pair<Long, Long> elapsedRuntimes = convertRtcBoundsToElapsed(rtcRuntimes, elapsedNow);
 
             if (XML_TAG_PERIODIC.equals(parser.getName())) {
@@ -862,22 +861,38 @@
         }
 
         private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) {
-            String val = parser.getAttributeValue(null, "connectivity");
-            if (val != null) {
-                jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+            String val;
+
+            final String netCapabilities = parser.getAttributeValue(null, "net-capabilities");
+            final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types");
+            if (netCapabilities != null && netTransportTypes != null) {
+                final NetworkRequest request = new NetworkRequest.Builder().build();
+                // We're okay throwing NFE here; caught by caller
+                request.networkCapabilities.setCapabilities(
+                        BitUtils.unpackBits(Long.parseLong(netCapabilities)));
+                request.networkCapabilities.setTransportTypes(
+                        BitUtils.unpackBits(Long.parseLong(netTransportTypes)));
+                jobBuilder.setRequiredNetwork(request);
+            } else {
+                // Read legacy values
+                val = parser.getAttributeValue(null, "connectivity");
+                if (val != null) {
+                    jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+                }
+                val = parser.getAttributeValue(null, "metered");
+                if (val != null) {
+                    jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED);
+                }
+                val = parser.getAttributeValue(null, "unmetered");
+                if (val != null) {
+                    jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
+                }
+                val = parser.getAttributeValue(null, "not-roaming");
+                if (val != null) {
+                    jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING);
+                }
             }
-            val = parser.getAttributeValue(null, "metered");
-            if (val != null) {
-                jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED);
-            }
-            val = parser.getAttributeValue(null, "unmetered");
-            if (val != null) {
-                jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
-            }
-            val = parser.getAttributeValue(null, "not-roaming");
-            if (val != null) {
-                jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING);
-            }
+
             val = parser.getAttributeValue(null, "idle");
             if (val != null) {
                 jobBuilder.setRequiresDeviceIdle(true);
@@ -937,8 +952,8 @@
         private Pair<Long, Long> buildExecutionTimesFromXml(XmlPullParser parser)
                 throws NumberFormatException {
             // Pull out execution time data.
-            final long nowWallclock = System.currentTimeMillis();
-            final long nowElapsed = SystemClock.elapsedRealtime();
+            final long nowWallclock = sSystemClock.millis();
+            final long nowElapsed = sElapsedRealtimeClock.millis();
 
             long earliestRunTimeElapsed = JobStatus.NO_EARLIEST_RUNTIME;
             long latestRunTimeElapsed = JobStatus.NO_LATEST_RUNTIME;
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 39f2a96..caa8522 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -174,7 +174,7 @@
     private final class AppIdleStateChangeListener
             extends UsageStatsManagerInternal.AppIdleStateChangeListener {
         @Override
-        public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
+        public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
             boolean changed = false;
             synchronized (mLock) {
                 if (mAppIdleParoleOn) {
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 9111969..76ff834 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -16,13 +16,14 @@
 
 package com.android.server.job.controllers;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.BatteryManager;
 import android.os.BatteryManagerInternal;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -203,7 +204,7 @@
                 if (Intent.ACTION_BATTERY_LOW.equals(action)) {
                     if (DEBUG) {
                         Slog.d(TAG, "Battery life too low to do work. @ "
-                                + SystemClock.elapsedRealtime());
+                                + sElapsedRealtimeClock.millis());
                     }
                     // If we get this action, the battery is discharging => it isn't plugged in so
                     // there's no work to cancel. We track this variable for the case where it is
@@ -213,14 +214,14 @@
                 } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
                     if (DEBUG) {
                         Slog.d(TAG, "Battery life healthy enough to do work. @ "
-                                + SystemClock.elapsedRealtime());
+                                + sElapsedRealtimeClock.millis());
                     }
                     mBatteryHealthy = true;
                     maybeReportNewChargingStateLocked();
                 } else if (BatteryManager.ACTION_CHARGING.equals(action)) {
                     if (DEBUG) {
                         Slog.d(TAG, "Received charging intent, fired @ "
-                                + SystemClock.elapsedRealtime());
+                                + sElapsedRealtimeClock.millis());
                     }
                     mCharging = true;
                     maybeReportNewChargingStateLocked();
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index ddee345..da28769 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -54,7 +54,6 @@
     private final ConnectivityManager mConnManager;
     private final NetworkPolicyManager mNetPolicyManager;
     private boolean mConnected;
-    private boolean mValidated;
 
     @GuardedBy("mLock")
     private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>();
@@ -79,7 +78,7 @@
         mConnManager = mContext.getSystemService(ConnectivityManager.class);
         mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
 
-        mConnected = mValidated = false;
+        mConnected = false;
 
         mConnManager.registerDefaultNetworkCallback(mNetworkCallback);
         mNetPolicyManager.registerListener(mNetPolicyListener);
@@ -149,32 +148,21 @@
     }
 
     private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
+        // TODO: consider matching against non-active networks
+
         final int jobUid = jobStatus.getSourceUid();
         final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
         final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked);
         final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobUid, ignoreBlocked);
-
-        final NetworkCapabilities capabilities = (network != null)
-                ? mConnManager.getNetworkCapabilities(network) : null;
+        final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
 
         final boolean connected = (info != null) && info.isConnected();
-        final boolean validated = (capabilities != null)
-                && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+        final boolean satisfied = jobStatus.getJob().getRequiredNetwork().networkCapabilities
+                .satisfiedByNetworkCapabilities(capabilities);
         final boolean sane = isSane(jobStatus, capabilities);
-        final boolean connectionUsable = connected && validated && sane;
 
-        final boolean metered = connected && (capabilities != null)
-                && !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-        final boolean unmetered = connected && (capabilities != null)
-                && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-        final boolean notRoaming = connected && (info != null)
-                && !info.isRoaming();
-
-        boolean changed = false;
-        changed |= jobStatus.setConnectivityConstraintSatisfied(connectionUsable);
-        changed |= jobStatus.setMeteredConstraintSatisfied(metered);
-        changed |= jobStatus.setUnmeteredConstraintSatisfied(unmetered);
-        changed |= jobStatus.setNotRoamingConstraintSatisfied(notRoaming);
+        final boolean changed = jobStatus
+                .setConnectivityConstraintSatisfied(connected && satisfied && sane);
 
         // Pass along the evaluated network for job to use; prevents race
         // conditions as default routes change over time, and opens the door to
@@ -185,17 +173,13 @@
         // overall state of connectivity constraint satisfiability.
         if (jobUid == Process.SYSTEM_UID) {
             mConnected = connected;
-            mValidated = validated;
         }
 
         if (DEBUG) {
             Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged")
-                    + " for " + jobStatus + ": usable=" + connectionUsable
-                    + " connected=" + connected
-                    + " validated=" + validated
-                    + " metered=" + metered
-                    + " unmetered=" + unmetered
-                    + " notRoaming=" + notRoaming);
+                    + " for " + jobStatus + ": connected=" + connected
+                    + " satisfied=" + satisfied
+                    + " sane=" + sane);
         }
         return changed;
     }
@@ -292,8 +276,6 @@
     public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
         pw.print("Connectivity: connected=");
         pw.print(mConnected);
-        pw.print(" validated=");
-        pw.println(mValidated);
         pw.print("Tracking ");
         pw.print(mTrackedJobs.size());
         pw.println(":");
@@ -304,10 +286,7 @@
                 js.printUniqueId(pw);
                 pw.print(" from ");
                 UserHandle.formatUid(pw, js.getSourceUid());
-                pw.print(": C="); pw.print(js.needsAnyConnectivity());
-                pw.print(": M="); pw.print(js.needsMeteredConnectivity());
-                pw.print(": UM="); pw.print(js.needsUnmeteredConnectivity());
-                pw.print(": NR="); pw.println(js.needsNonRoamingConnectivity());
+                pw.print(": "); pw.print(js.getJob().getRequiredNetwork());
             }
         }
     }
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 9eda046..7bde174 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -16,7 +16,7 @@
 
 package com.android.server.job.controllers;
 
-import java.io.PrintWriter;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -24,7 +24,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -33,6 +32,8 @@
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
 
+import java.io.PrintWriter;
+
 public final class IdleController extends StateController {
     private static final String TAG = "IdleController";
 
@@ -169,7 +170,7 @@
                 // when the screen goes off or dreaming starts, we schedule the
                 // alarm that will tell us when we have decided the device is
                 // truly idle.
-                final long nowElapsed = SystemClock.elapsedRealtime();
+                final long nowElapsed = sElapsedRealtimeClock.millis();
                 final long when = nowElapsed + mInactivityIdleThreshold;
                 if (DEBUG) {
                     Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
@@ -182,7 +183,7 @@
                 // idle time starts now. Do not set mIdle if screen is on.
                 if (!mIdle && !mScreenOn) {
                     if (DEBUG) {
-                        Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime());
+                        Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
                     }
                     mIdle = true;
                     reportNewIdleState(mIdle);
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 46ed84e..a5906cb 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -16,6 +16,8 @@
 
 package com.android.server.job.controllers;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
 import android.app.AppGlobals;
 import android.app.IActivityManager;
 import android.app.job.JobInfo;
@@ -25,7 +27,6 @@
 import android.net.Network;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.Time;
 import android.util.ArraySet;
@@ -64,19 +65,12 @@
     static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW;
     static final int CONSTRAINT_TIMING_DELAY = 1<<31;
     static final int CONSTRAINT_DEADLINE = 1<<30;
-    static final int CONSTRAINT_UNMETERED = 1<<29;
     static final int CONSTRAINT_CONNECTIVITY = 1<<28;
     static final int CONSTRAINT_APP_NOT_IDLE = 1<<27;
     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25;
-    static final int CONSTRAINT_NOT_ROAMING = 1<<24;
-    static final int CONSTRAINT_METERED = 1<<23;
     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22;
 
-    static final int CONNECTIVITY_MASK =
-            CONSTRAINT_UNMETERED | CONSTRAINT_CONNECTIVITY |
-            CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED;
-
     // Soft override: ignore constraints like time that don't affect API availability
     public static final int OVERRIDE_SOFT = 1;
     // Full override: ignore all constraints including API-affecting like connectivity
@@ -264,27 +258,9 @@
 
         int requiredConstraints = job.getConstraintFlags();
 
-        switch (job.getNetworkType()) {
-            case JobInfo.NETWORK_TYPE_NONE:
-                // No constraint.
-                break;
-            case JobInfo.NETWORK_TYPE_ANY:
-                requiredConstraints |= CONSTRAINT_CONNECTIVITY;
-                break;
-            case JobInfo.NETWORK_TYPE_UNMETERED:
-                requiredConstraints |= CONSTRAINT_UNMETERED;
-                break;
-            case JobInfo.NETWORK_TYPE_NOT_ROAMING:
-                requiredConstraints |= CONSTRAINT_NOT_ROAMING;
-                break;
-            case JobInfo.NETWORK_TYPE_METERED:
-                requiredConstraints |= CONSTRAINT_METERED;
-                break;
-            default:
-                Slog.w(TAG, "Unrecognized networking constraint " + job.getNetworkType());
-                break;
+        if (job.getRequiredNetwork() != null) {
+            requiredConstraints |= CONSTRAINT_CONNECTIVITY;
         }
-
         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
             requiredConstraints |= CONSTRAINT_TIMING_DELAY;
         }
@@ -365,7 +341,7 @@
      */
     public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName,
             int sourceUserId, String tag) {
-        final long elapsedNow = SystemClock.elapsedRealtime();
+        final long elapsedNow = sElapsedRealtimeClock.millis();
         final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
         if (job.isPeriodic()) {
             latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
@@ -610,25 +586,9 @@
 
     /** Does this job have any sort of networking constraint? */
     public boolean hasConnectivityConstraint() {
-        return (requiredConstraints&CONNECTIVITY_MASK) != 0;
-    }
-
-    public boolean needsAnyConnectivity() {
         return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
     }
 
-    public boolean needsUnmeteredConnectivity() {
-        return (requiredConstraints&CONSTRAINT_UNMETERED) != 0;
-    }
-
-    public boolean needsMeteredConnectivity() {
-        return (requiredConstraints&CONSTRAINT_METERED) != 0;
-    }
-
-    public boolean needsNonRoamingConnectivity() {
-        return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0;
-    }
-
     public boolean hasChargingConstraint() {
         return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
     }
@@ -725,18 +685,6 @@
         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
     }
 
-    boolean setUnmeteredConstraintSatisfied(boolean state) {
-        return setConstraintSatisfied(CONSTRAINT_UNMETERED, state);
-    }
-
-    boolean setMeteredConstraintSatisfied(boolean state) {
-        return setConstraintSatisfied(CONSTRAINT_METERED, state);
-    }
-
-    boolean setNotRoamingConstraintSatisfied(boolean state) {
-        return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state);
-    }
-
     boolean setAppNotIdleConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state);
     }
@@ -817,12 +765,9 @@
                 && notRestrictedInBg;
     }
 
-    static final int CONSTRAINTS_OF_INTEREST =
-            CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW |
-            CONSTRAINT_TIMING_DELAY |
-            CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED |
-            CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED |
-            CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
+    static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
+            | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY
+            | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
 
     // Soft override covers all non-"functional" constraints
     static final int SOFT_OVERRIDE_CONSTRAINTS =
@@ -870,15 +815,14 @@
         sb.append(getSourceUid());
         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME
                 || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
-            long now = SystemClock.elapsedRealtime();
+            long now = sElapsedRealtimeClock.millis();
             sb.append(" TIME=");
             formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now);
             sb.append(":");
             formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now);
         }
-        if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
-            sb.append(" NET=");
-            sb.append(job.getNetworkType());
+        if (job.getRequiredNetwork() != null) {
+            sb.append(" NET");
         }
         if (job.isRequireCharging()) {
             sb.append(" CHARGING");
@@ -985,15 +929,6 @@
         if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
             pw.print(" CONNECTIVITY");
         }
-        if ((constraints&CONSTRAINT_UNMETERED) != 0) {
-            pw.print(" UNMETERED");
-        }
-        if ((constraints&CONSTRAINT_NOT_ROAMING) != 0) {
-            pw.print(" NOT_ROAMING");
-        }
-        if ((constraints&CONSTRAINT_METERED) != 0) {
-            pw.print(" METERED");
-        }
         if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) {
             pw.print(" APP_NOT_IDLE");
         }
@@ -1084,11 +1019,13 @@
                 pw.print(prefix); pw.println("  Granted URI permissions:");
                 uriPerms.dump(pw, prefix + "  ");
             }
-            if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
-                pw.print(prefix); pw.print("  Network type: "); pw.println(job.getNetworkType());
+            if (job.getRequiredNetwork() != null) {
+                pw.print(prefix); pw.print("  Network type: ");
+                pw.println(job.getRequiredNetwork());
             }
             if (totalNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
-                pw.print(prefix); pw.print("  Network bytes: "); pw.println(totalNetworkBytes);
+                pw.print(prefix); pw.print("  Network bytes: ");
+                pw.println(totalNetworkBytes);
             }
             if (job.getMinLatencyMillis() != 0) {
                 pw.print(prefix); pw.print("  Minimum latency: ");
diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java
index c24e563..84782f5 100644
--- a/services/core/java/com/android/server/job/controllers/StorageController.java
+++ b/services/core/java/com/android/server/job/controllers/StorageController.java
@@ -16,11 +16,12 @@
 
 package com.android.server.job.controllers;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -154,13 +155,13 @@
             if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
                 if (DEBUG) {
                     Slog.d(TAG, "Available storage too low to do work. @ "
-                            + SystemClock.elapsedRealtime());
+                            + sElapsedRealtimeClock.millis());
                 }
                 mStorageLow = true;
             } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
                 if (DEBUG) {
                     Slog.d(TAG, "Available stoage high enough to do work. @ "
-                            + SystemClock.elapsedRealtime());
+                            + sElapsedRealtimeClock.millis());
                 }
                 mStorageLow = false;
                 maybeReportNewStorageState();
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index ee4c606..cb9e43a 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -16,10 +16,11 @@
 
 package com.android.server.job.controllers;
 
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
 import android.content.Context;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.util.Slog;
@@ -84,7 +85,7 @@
             // pattern of having a job with a 0 deadline constraint ("run immediately").
             // Unlike most controllers, once one of our constraints has been satisfied, it
             // will never be unsatisfied (our time base can not go backwards).
-            final long nowElapsedMillis = SystemClock.elapsedRealtime();
+            final long nowElapsedMillis = sElapsedRealtimeClock.millis();
             if (job.hasDeadlineConstraint() && evaluateDeadlineConstraint(job, nowElapsedMillis)) {
                 return;
             } else if (job.hasTimingDelayConstraint() && evaluateTimingDelayConstraint(job,
@@ -157,7 +158,7 @@
             long nextExpiryTime = Long.MAX_VALUE;
             int nextExpiryUid = 0;
             String nextExpiryPackageName = null;
-            final long nowElapsedMillis = SystemClock.elapsedRealtime();
+            final long nowElapsedMillis = sElapsedRealtimeClock.millis();
 
             Iterator<JobStatus> it = mTrackedJobs.iterator();
             while (it.hasNext()) {
@@ -201,7 +202,7 @@
      */
     private void checkExpiredDelaysAndResetAlarm() {
         synchronized (mLock) {
-            final long nowElapsedMillis = SystemClock.elapsedRealtime();
+            final long nowElapsedMillis = sElapsedRealtimeClock.millis();
             long nextDelayTime = Long.MAX_VALUE;
             int nextDelayUid = 0;
             String nextDelayPackageName = null;
@@ -283,7 +284,7 @@
     }
 
     private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
-        final long earliestWakeupTimeElapsed = SystemClock.elapsedRealtime();
+        final long earliestWakeupTimeElapsed = sElapsedRealtimeClock.millis();
         if (proposedAlarmTimeElapsedMillis < earliestWakeupTimeElapsed) {
             return earliestWakeupTimeElapsed;
         }
@@ -328,9 +329,9 @@
 
     @Override
     public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
-        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long nowElapsed = sElapsedRealtimeClock.millis();
         pw.print("Alarms: now=");
-        pw.print(SystemClock.elapsedRealtime());
+        pw.print(sElapsedRealtimeClock.millis());
         pw.println();
         pw.print("Next delay alarm in ");
         TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 551cb10..3fa3cd4 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3746,7 +3746,7 @@
             extends UsageStatsManagerInternal.AppIdleStateChangeListener {
 
         @Override
-        public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
+        public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
             try {
                 final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
                         PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
diff --git a/services/core/java/com/android/server/net/watchlist/DigestUtils.java b/services/core/java/com/android/server/net/watchlist/DigestUtils.java
new file mode 100644
index 0000000..57becb0
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/DigestUtils.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.net.watchlist;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Utils for calculating digests.
+ */
+public class DigestUtils {
+
+    private static final int FILE_READ_BUFFER_SIZE = 16 * 1024;
+
+    private DigestUtils() {}
+
+    /** @return SHA256 hash of the provided file */
+    public static byte[] getSha256Hash(File apkFile) throws IOException, NoSuchAlgorithmException {
+        try (InputStream stream = new FileInputStream(apkFile)) {
+            return getSha256Hash(stream);
+        }
+    }
+
+    /** @return SHA256 hash of data read from the provided input stream */
+    public static byte[] getSha256Hash(InputStream stream)
+            throws IOException, NoSuchAlgorithmException {
+        MessageDigest digester = MessageDigest.getInstance("SHA256");
+
+        int bytesRead;
+        byte[] buf = new byte[FILE_READ_BUFFER_SIZE];
+        while ((bytesRead = stream.read(buf)) >= 0) {
+            digester.update(buf, 0, bytesRead);
+        }
+        return digester.digest();
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java b/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java
new file mode 100644
index 0000000..27c22ce
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java
@@ -0,0 +1,55 @@
+/*
+ * 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.net.watchlist;
+
+import com.android.internal.util.HexDump;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class to store all harmful digests in memory.
+ * TODO: Optimize memory usage using byte array with binary search.
+ */
+class HarmfulDigests {
+
+    private final Set<String> mDigestSet;
+
+    HarmfulDigests(List<byte[]> digests) {
+        final HashSet<String> tmpDigestSet = new HashSet<>();
+        final int size = digests.size();
+        for (int i = 0; i < size; i++) {
+            tmpDigestSet.add(HexDump.toHexString(digests.get(i)));
+        }
+        mDigestSet = Collections.unmodifiableSet(tmpDigestSet);
+    }
+
+    public boolean contains(byte[] digest) {
+        return mDigestSet.contains(HexDump.toHexString(digest));
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        for (String digest : mDigestSet) {
+            pw.println(digest);
+        }
+        pw.println("");
+    }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
new file mode 100644
index 0000000..171703a
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -0,0 +1,267 @@
+/*
+ * 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.net.watchlist;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
+import android.net.NetworkWatchlistManager;
+import android.net.metrics.IpConnectivityLog;
+import android.os.Binder;
+import android.os.Process;
+import android.os.SharedMemory;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.net.INetworkWatchlistManager;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Implementation of network watchlist service.
+ */
+public class NetworkWatchlistService extends INetworkWatchlistManager.Stub {
+
+    private static final String TAG = NetworkWatchlistService.class.getSimpleName();
+    static final boolean DEBUG = false;
+
+    private static final String PROPERTY_NETWORK_WATCHLIST_ENABLED =
+            "ro.network_watchlist_enabled";
+
+    private static final int MAX_NUM_OF_WATCHLIST_DIGESTS = 10000;
+
+    public static class Lifecycle extends SystemService {
+        private NetworkWatchlistService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
+                // Watchlist service is disabled
+                return;
+            }
+            mService = new NetworkWatchlistService(getContext());
+            publishBinderService(Context.NETWORK_WATCHLIST_SERVICE, mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
+                // Watchlist service is disabled
+                return;
+            }
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                try {
+                    mService.initIpConnectivityMetrics();
+                    mService.startWatchlistLogging();
+                } catch (RemoteException e) {
+                    // Should not happen
+                }
+                ReportWatchlistJobService.schedule(getContext());
+            }
+        }
+    }
+
+    private volatile boolean mIsLoggingEnabled = false;
+    private final Object mLoggingSwitchLock = new Object();
+
+    private final WatchlistSettings mSettings;
+    private final Context mContext;
+
+    // Separate thread to handle expensive watchlist logging work.
+    private final ServiceThread mHandlerThread;
+
+    @VisibleForTesting
+    IIpConnectivityMetrics mIpConnectivityMetrics;
+    @VisibleForTesting
+    WatchlistLoggingHandler mNetworkWatchlistHandler;
+
+    public NetworkWatchlistService(Context context) {
+        mContext = context;
+        mSettings = WatchlistSettings.getInstance();
+        mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
+                        /* allowIo */ false);
+        mHandlerThread.start();
+        mNetworkWatchlistHandler = new WatchlistLoggingHandler(mContext,
+                mHandlerThread.getLooper());
+        mNetworkWatchlistHandler.reportWatchlistIfNecessary();
+    }
+
+    // For testing only
+    @VisibleForTesting
+    NetworkWatchlistService(Context context, ServiceThread handlerThread,
+            WatchlistLoggingHandler handler, IIpConnectivityMetrics ipConnectivityMetrics) {
+        mContext = context;
+        mSettings = WatchlistSettings.getInstance();
+        mHandlerThread = handlerThread;
+        mNetworkWatchlistHandler = handler;
+        mIpConnectivityMetrics = ipConnectivityMetrics;
+    }
+
+    private void initIpConnectivityMetrics() {
+        mIpConnectivityMetrics = (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+                ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+    }
+
+    private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
+        @Override
+        public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
+                long timestamp, int uid) {
+            if (!mIsLoggingEnabled) {
+                return;
+            }
+            mNetworkWatchlistHandler.asyncNetworkEvent(hostname, ipAddresses, uid);
+        }
+
+        @Override
+        public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
+            if (!mIsLoggingEnabled) {
+                return;
+            }
+            mNetworkWatchlistHandler.asyncNetworkEvent(null, new String[]{ipAddr}, uid);
+        }
+    };
+
+    @VisibleForTesting
+    protected boolean startWatchlistLoggingImpl() throws RemoteException {
+        if (DEBUG) {
+            Slog.i(TAG, "Starting watchlist logging.");
+        }
+        synchronized (mLoggingSwitchLock) {
+            if (mIsLoggingEnabled) {
+                Slog.w(TAG, "Watchlist logging is already running");
+                return true;
+            }
+            try {
+                if (mIpConnectivityMetrics.addNetdEventCallback(
+                        INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST, mNetdEventCallback)) {
+                    mIsLoggingEnabled = true;
+                    return true;
+                } else {
+                    return false;
+                }
+            } catch (RemoteException re) {
+                // Should not happen
+                return false;
+            }
+        }
+    }
+
+    @Override
+    public boolean startWatchlistLogging() throws RemoteException {
+        enforceWatchlistLoggingPermission();
+        return startWatchlistLoggingImpl();
+    }
+
+    @VisibleForTesting
+    protected boolean stopWatchlistLoggingImpl() {
+        if (DEBUG) {
+            Slog.i(TAG, "Stopping watchlist logging");
+        }
+        synchronized (mLoggingSwitchLock) {
+            if (!mIsLoggingEnabled) {
+                Slog.w(TAG, "Watchlist logging is not running");
+                return true;
+            }
+            // stop the logging regardless of whether we fail to unregister listener
+            mIsLoggingEnabled = false;
+
+            try {
+                return mIpConnectivityMetrics.removeNetdEventCallback(
+                        INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+            } catch (RemoteException re) {
+                // Should not happen
+                return false;
+            }
+        }
+    }
+
+    @Override
+    public boolean stopWatchlistLogging() throws RemoteException {
+        enforceWatchlistLoggingPermission();
+        return stopWatchlistLoggingImpl();
+    }
+
+    private void enforceWatchlistLoggingPermission() {
+        final int uid = Binder.getCallingUid();
+        if (uid != Process.SYSTEM_UID) {
+            throw new SecurityException(String.format("Uid %d has no permission to change watchlist"
+                    + " setting.", uid));
+        }
+    }
+
+    /**
+     * Set a new network watchlist.
+     * This method should be called by ConfigUpdater only.
+     *
+     * @return True if network watchlist is updated.
+     */
+    public boolean setNetworkSecurityWatchlist(List<byte[]> domainsCrc32Digests,
+            List<byte[]> domainsSha256Digests,
+            List<byte[]> ipAddressesCrc32Digests,
+            List<byte[]> ipAddressesSha256Digests) {
+        Slog.i(TAG, "Setting network watchlist");
+        if (domainsCrc32Digests == null || domainsSha256Digests == null
+                || ipAddressesCrc32Digests == null || ipAddressesSha256Digests == null) {
+            Slog.e(TAG, "Parameters cannot be null");
+            return false;
+        }
+        if (domainsCrc32Digests.size() != domainsSha256Digests.size()
+                || ipAddressesCrc32Digests.size() != ipAddressesSha256Digests.size()) {
+            Slog.e(TAG, "Must need to have the same number of CRC32 and SHA256 digests");
+            return false;
+        }
+        if (domainsSha256Digests.size() + ipAddressesSha256Digests.size()
+                > MAX_NUM_OF_WATCHLIST_DIGESTS) {
+            Slog.e(TAG, "Total watchlist size cannot exceed " + MAX_NUM_OF_WATCHLIST_DIGESTS);
+            return false;
+        }
+        mSettings.writeSettingsToDisk(domainsCrc32Digests, domainsSha256Digests,
+                ipAddressesCrc32Digests, ipAddressesSha256Digests);
+        Slog.i(TAG, "Set network watchlist: Success");
+        return true;
+    }
+
+    @Override
+    public void reportWatchlistIfNecessary() {
+        // Allow any apps to trigger report event, as we won't run it if it's too early.
+        mNetworkWatchlistHandler.reportWatchlistIfNecessary();
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        mSettings.dump(fd, pw, args);
+    }
+
+}
diff --git a/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java b/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java
new file mode 100644
index 0000000..dfeb1b2
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java
@@ -0,0 +1,76 @@
+/*
+ * 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.net.watchlist;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.NetworkWatchlistManager;
+import android.util.Slog;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A job that periodically report watchlist records.
+ */
+public class ReportWatchlistJobService extends JobService {
+
+    private static final boolean DEBUG = NetworkWatchlistService.DEBUG;
+    private static final String TAG = "WatchlistJobService";
+
+    // Unique job id used in system service, other jobs should not use the same value.
+    public static final int REPORT_WATCHLIST_RECORDS_JOB_ID = 0xd7689;
+    public static final long REPORT_WATCHLIST_RECORDS_PERIOD_MILLIS =
+            TimeUnit.HOURS.toMillis(12);
+
+    @Override
+    public boolean onStartJob(final JobParameters jobParameters) {
+        if (jobParameters.getJobId() != REPORT_WATCHLIST_RECORDS_JOB_ID) {
+            return false;
+        }
+        if (DEBUG) Slog.d(TAG, "Start scheduled job.");
+        new NetworkWatchlistManager(this).reportWatchlistIfNecessary();
+        jobFinished(jobParameters, false);
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters jobParameters) {
+        return true; // Reschedule when possible.
+    }
+
+    /**
+     * Schedule the {@link ReportWatchlistJobService} to run periodically.
+     */
+    public static void schedule(Context context) {
+        if (DEBUG) Slog.d(TAG, "Scheduling records aggregator task");
+        final JobScheduler scheduler =
+                (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        scheduler.schedule(new JobInfo.Builder(REPORT_WATCHLIST_RECORDS_JOB_ID,
+                new ComponentName(context, ReportWatchlistJobService.class))
+                //.setOverrideDeadline(45 * 1000) // Schedule job soon, for testing.
+                .setPeriodic(REPORT_WATCHLIST_RECORDS_PERIOD_MILLIS)
+                .setRequiresDeviceIdle(true)
+                .setRequiresBatteryNotLow(true)
+                .setPersisted(false)
+                .build());
+    }
+
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
new file mode 100644
index 0000000..2247558
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -0,0 +1,298 @@
+/*
+ * 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.net.watchlist;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.DropBoxManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A Handler class for network watchlist logging on a background thread.
+ */
+class WatchlistLoggingHandler extends Handler {
+
+    private static final String TAG = WatchlistLoggingHandler.class.getSimpleName();
+    private static final boolean DEBUG = NetworkWatchlistService.DEBUG;
+
+    @VisibleForTesting
+    static final int LOG_WATCHLIST_EVENT_MSG = 1;
+    @VisibleForTesting
+    static final int REPORT_RECORDS_IF_NECESSARY_MSG = 2;
+
+    private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1);
+    private static final String DROPBOX_TAG = "network_watchlist_report";
+
+    private final Context mContext;
+    private final ContentResolver mResolver;
+    private final PackageManager mPm;
+    private final WatchlistReportDbHelper mDbHelper;
+    private final WatchlistSettings mSettings;
+    // A cache for uid and apk digest mapping.
+    // As uid won't be reused until reboot, it's safe to assume uid is unique per signature and app.
+    // TODO: Use more efficient data structure.
+    private final HashMap<Integer, byte[]> mCachedUidDigestMap = new HashMap<>();
+
+    private interface WatchlistEventKeys {
+        String HOST = "host";
+        String IP_ADDRESSES = "ipAddresses";
+        String UID = "uid";
+        String TIMESTAMP = "timestamp";
+    }
+
+    WatchlistLoggingHandler(Context context, Looper looper) {
+        super(looper);
+        mContext = context;
+        mPm = mContext.getPackageManager();
+        mResolver = mContext.getContentResolver();
+        mDbHelper = WatchlistReportDbHelper.getInstance(context);
+        mSettings = WatchlistSettings.getInstance();
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case LOG_WATCHLIST_EVENT_MSG: {
+                final Bundle data = msg.getData();
+                handleNetworkEvent(
+                        data.getString(WatchlistEventKeys.HOST),
+                        data.getStringArray(WatchlistEventKeys.IP_ADDRESSES),
+                        data.getInt(WatchlistEventKeys.UID),
+                        data.getLong(WatchlistEventKeys.TIMESTAMP)
+                );
+                break;
+            }
+            case REPORT_RECORDS_IF_NECESSARY_MSG:
+                tryAggregateRecords();
+                break;
+            default: {
+                Slog.d(TAG, "WatchlistLoggingHandler received an unknown of message.");
+                break;
+            }
+        }
+    }
+
+    /**
+     * Report network watchlist records if we collected enough data.
+     */
+    public void reportWatchlistIfNecessary() {
+        final Message msg = obtainMessage(REPORT_RECORDS_IF_NECESSARY_MSG);
+        sendMessage(msg);
+    }
+
+    /**
+     * Insert network traffic event to watchlist async queue processor.
+     */
+    public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+        final Message msg = obtainMessage(LOG_WATCHLIST_EVENT_MSG);
+        final Bundle bundle = new Bundle();
+        bundle.putString(WatchlistEventKeys.HOST, host);
+        bundle.putStringArray(WatchlistEventKeys.IP_ADDRESSES, ipAddresses);
+        bundle.putInt(WatchlistEventKeys.UID, uid);
+        bundle.putLong(WatchlistEventKeys.TIMESTAMP, System.currentTimeMillis());
+        msg.setData(bundle);
+        sendMessage(msg);
+    }
+
+    private void handleNetworkEvent(String hostname, String[] ipAddresses,
+            int uid, long timestamp) {
+        if (DEBUG) {
+            Slog.i(TAG, "handleNetworkEvent with host: " + hostname + ", uid: " + uid);
+        }
+        final String cncDomain = searchAllSubDomainsInWatchlist(hostname);
+        if (cncDomain != null) {
+            insertRecord(getDigestFromUid(uid), cncDomain, timestamp);
+        } else {
+            final String cncIp = searchIpInWatchlist(ipAddresses);
+            if (cncIp != null) {
+                insertRecord(getDigestFromUid(uid), cncIp, timestamp);
+            }
+        }
+    }
+
+    private boolean insertRecord(byte[] digest, String cncHost, long timestamp) {
+        final boolean result = mDbHelper.insertNewRecord(digest, cncHost, timestamp);
+        tryAggregateRecords();
+        return result;
+    }
+
+    private boolean shouldReportNetworkWatchlist() {
+        final long lastReportTime = Settings.Global.getLong(mResolver,
+                Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, 0L);
+        final long currentTimestamp = System.currentTimeMillis();
+        if (currentTimestamp < lastReportTime) {
+            Slog.i(TAG, "Last report time is larger than current time, reset report");
+            mDbHelper.cleanup();
+            return false;
+        }
+        return currentTimestamp >= lastReportTime + ONE_DAY_MS;
+    }
+
+    private void tryAggregateRecords() {
+        if (shouldReportNetworkWatchlist()) {
+            Slog.i(TAG, "Start aggregating watchlist records.");
+            final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+            if (dbox != null && !dbox.isTagEnabled(DROPBOX_TAG)) {
+                final WatchlistReportDbHelper.AggregatedResult aggregatedResult =
+                        mDbHelper.getAggregatedRecords();
+                final byte[] encodedResult = encodeAggregatedResult(aggregatedResult);
+                if (encodedResult != null) {
+                    addEncodedReportToDropBox(encodedResult);
+                }
+            }
+            mDbHelper.cleanup();
+            Settings.Global.putLong(mResolver, Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
+                    System.currentTimeMillis());
+        } else {
+            Slog.i(TAG, "No need to aggregate record yet.");
+        }
+    }
+
+    private byte[] encodeAggregatedResult(
+            WatchlistReportDbHelper.AggregatedResult aggregatedResult) {
+        // TODO: Encode results using differential privacy.
+        return null;
+    }
+
+    private void addEncodedReportToDropBox(byte[] encodedReport) {
+        final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+        dbox.addData(DROPBOX_TAG, encodedReport, 0);
+    }
+
+    /**
+     * Get app digest from app uid.
+     */
+    private byte[] getDigestFromUid(int uid) {
+        final byte[] cachedDigest = mCachedUidDigestMap.get(uid);
+        if (cachedDigest != null) {
+            return cachedDigest;
+        }
+        final String[] packageNames = mPm.getPackagesForUid(uid);
+        final int userId = UserHandle.getUserId(uid);
+        if (!ArrayUtils.isEmpty(packageNames)) {
+            for (String packageName : packageNames) {
+                try {
+                    final String apkPath = mPm.getPackageInfoAsUser(packageName,
+                            PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId)
+                            .applicationInfo.publicSourceDir;
+                    if (TextUtils.isEmpty(apkPath)) {
+                        Slog.w(TAG, "Cannot find apkPath for " + packageName);
+                        continue;
+                    }
+                    final byte[] digest = DigestUtils.getSha256Hash(new File(apkPath));
+                    mCachedUidDigestMap.put(uid, digest);
+                    return digest;
+                } catch (NameNotFoundException | NoSuchAlgorithmException | IOException e) {
+                    Slog.e(TAG, "Should not happen", e);
+                    return null;
+                }
+            }
+        } else {
+            Slog.e(TAG, "Should not happen");
+        }
+        return null;
+    }
+
+    /**
+     * Search if any ip addresses are in watchlist.
+     *
+     * @param ipAddresses Ip address that you want to search in watchlist.
+     * @return Ip address that exists in watchlist, null if it does not match anything.
+     */
+    private String searchIpInWatchlist(String[] ipAddresses) {
+        for (String ipAddress : ipAddresses) {
+            if (isIpInWatchlist(ipAddress)) {
+                return ipAddress;
+            }
+        }
+        return null;
+    }
+
+    /** Search if the ip is in watchlist */
+    private boolean isIpInWatchlist(String ipAddr) {
+        if (ipAddr == null) {
+            return false;
+        }
+        return mSettings.containsIp(ipAddr);
+    }
+
+    /** Search if the host is in watchlist */
+    private boolean isHostInWatchlist(String host) {
+        if (host == null) {
+            return false;
+        }
+        return mSettings.containsDomain(host);
+    }
+
+    /**
+     * Search if any sub-domain in host is in watchlist.
+     *
+     * @param host Host that we want to search.
+     * @return Domain that exists in watchlist, null if it does not match anything.
+     */
+    private String searchAllSubDomainsInWatchlist(String host) {
+        if (host == null) {
+            return null;
+        }
+        final String[] subDomains = getAllSubDomains(host);
+        for (String subDomain : subDomains) {
+            if (isHostInWatchlist(subDomain)) {
+                return subDomain;
+            }
+        }
+        return null;
+    }
+
+    /** Get all sub-domains in a host */
+    @VisibleForTesting
+    static String[] getAllSubDomains(String host) {
+        if (host == null) {
+            return null;
+        }
+        final ArrayList<String> subDomainList = new ArrayList<>();
+        subDomainList.add(host);
+        int index = host.indexOf(".");
+        while (index != -1) {
+            host = host.substring(index + 1);
+            if (!TextUtils.isEmpty(host)) {
+                subDomainList.add(host);
+            }
+            index = host.indexOf(".");
+        }
+        return subDomainList.toArray(new String[0]);
+    }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
new file mode 100644
index 0000000..f48463f
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -0,0 +1,203 @@
+/*
+ * 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.net.watchlist;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Pair;
+
+import com.android.internal.util.HexDump;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class to process watchlist read / save watchlist reports.
+ */
+class WatchlistReportDbHelper extends SQLiteOpenHelper {
+
+    private static final String TAG = "WatchlistReportDbHelper";
+
+    private static final String NAME = "watchlist_report.db";
+    private static final int VERSION = 2;
+
+    private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
+
+    private static class WhiteListReportContract {
+        private static final String TABLE = "records";
+        private static final String APP_DIGEST = "app_digest";
+        private static final String CNC_DOMAIN = "cnc_domain";
+        private static final String TIMESTAMP = "timestamp";
+    }
+
+    private static final String CREATE_TABLE_MODEL = "CREATE TABLE "
+            + WhiteListReportContract.TABLE + "("
+            + WhiteListReportContract.APP_DIGEST + " BLOB,"
+            + WhiteListReportContract.CNC_DOMAIN + " TEXT,"
+            + WhiteListReportContract.TIMESTAMP + " INTEGER DEFAULT 0" + " )";
+
+    private static final int INDEX_DIGEST = 0;
+    private static final int INDEX_CNC_DOMAIN = 1;
+    private static final int INDEX_TIMESTAMP = 2;
+
+    private static final String[] DIGEST_DOMAIN_PROJECTION =
+            new String[] {
+                    WhiteListReportContract.APP_DIGEST,
+                    WhiteListReportContract.CNC_DOMAIN
+            };
+
+    private static WatchlistReportDbHelper sInstance;
+
+    /**
+     * Aggregated watchlist records.
+     */
+    public static class AggregatedResult {
+        // A list of digests that visited c&c domain or ip before.
+        Set<String> appDigestList;
+
+        // The c&c domain or ip visited before.
+        String cncDomainVisited;
+
+        // A list of app digests and c&c domain visited.
+        HashMap<String, String> appDigestCNCList;
+    }
+
+    private WatchlistReportDbHelper(Context context) {
+        super(context, WatchlistSettings.getSystemWatchlistFile(NAME).getAbsolutePath(),
+                null, VERSION);
+        // Memory optimization - close idle connections after 30s of inactivity
+        setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+    }
+
+    public static synchronized WatchlistReportDbHelper getInstance(Context context) {
+        if (sInstance != null) {
+            return sInstance;
+        }
+        sInstance = new WatchlistReportDbHelper(context);
+        return sInstance;
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        db.execSQL(CREATE_TABLE_MODEL);
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        // TODO: For now, drop older tables and recreate new ones.
+        db.execSQL("DROP TABLE IF EXISTS " + WhiteListReportContract.TABLE);
+        onCreate(db);
+    }
+
+    /**
+     * Insert new watchlist record.
+     *
+     * @param appDigest The digest of an app.
+     * @param cncDomain C&C domain that app visited.
+     * @return True if success.
+     */
+    public boolean insertNewRecord(byte[] appDigest, String cncDomain,
+            long timestamp) {
+        final SQLiteDatabase db = getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(WhiteListReportContract.APP_DIGEST, appDigest);
+        values.put(WhiteListReportContract.CNC_DOMAIN, cncDomain);
+        values.put(WhiteListReportContract.TIMESTAMP, timestamp);
+        return db.insert(WhiteListReportContract.TABLE, null, values) != -1;
+    }
+
+    /**
+     * Aggregate the records in database, and return a rappor encoded result.
+     */
+    public AggregatedResult getAggregatedRecords() {
+        final long twoDaysBefore = getTwoDaysBeforeTimestamp();
+        final long yesterday = getYesterdayTimestamp();
+        final String selectStatement = WhiteListReportContract.TIMESTAMP + " >= ? AND " +
+                WhiteListReportContract.TIMESTAMP + " <= ?";
+
+        final SQLiteDatabase db = getReadableDatabase();
+        Cursor c = null;
+        try {
+            c = db.query(true /* distinct */,
+                    WhiteListReportContract.TABLE, DIGEST_DOMAIN_PROJECTION, selectStatement,
+                    new String[]{"" + twoDaysBefore, "" + yesterday}, null, null,
+                    null, null);
+            if (c == null || c.getCount() == 0) {
+                return null;
+            }
+            final AggregatedResult result = new AggregatedResult();
+            result.cncDomainVisited = null;
+            // After aggregation, each digest maximum will have only 1 record.
+            result.appDigestList = new HashSet<>();
+            result.appDigestCNCList = new HashMap<>();
+            while (c.moveToNext()) {
+                // We use hex string here as byte[] cannot be a key in HashMap.
+                String digestHexStr = HexDump.toHexString(c.getBlob(INDEX_DIGEST));
+                String cncDomain = c.getString(INDEX_CNC_DOMAIN);
+
+                result.appDigestList.add(digestHexStr);
+                if (result.cncDomainVisited != null) {
+                    result.cncDomainVisited = cncDomain;
+                }
+                result.appDigestCNCList.put(digestHexStr, cncDomain);
+            }
+            return result;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    /**
+     * Remove all the records before yesterday.
+     *
+     * @return True if success.
+     */
+    public boolean cleanup() {
+        final SQLiteDatabase db = getWritableDatabase();
+        final long twoDaysBefore = getTwoDaysBeforeTimestamp();
+        final String clause = WhiteListReportContract.TIMESTAMP + "< " + twoDaysBefore;
+        return db.delete(WhiteListReportContract.TABLE, clause, null) != 0;
+    }
+
+    static long getTwoDaysBeforeTimestamp() {
+        return getMidnightTimestamp(2);
+    }
+
+    static long getYesterdayTimestamp() {
+        return getMidnightTimestamp(1);
+    }
+
+    static long getMidnightTimestamp(int daysBefore) {
+        java.util.Calendar date = new GregorianCalendar();
+        // reset hour, minutes, seconds and millis
+        date.set(java.util.Calendar.HOUR_OF_DAY, 0);
+        date.set(java.util.Calendar.MINUTE, 0);
+        date.set(java.util.Calendar.SECOND, 0);
+        date.set(java.util.Calendar.MILLISECOND, 0);
+        date.add(java.util.Calendar.DAY_OF_MONTH, -daysBefore);
+        return date.getTimeInMillis();
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
new file mode 100644
index 0000000..c50f0d5
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -0,0 +1,284 @@
+/*
+ * 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.net.watchlist;
+
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.HexDump;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.CRC32;
+
+/**
+ * A util class to do watchlist settings operations, like setting watchlist, query if a domain
+ * exists in watchlist.
+ */
+class WatchlistSettings {
+    private static final String TAG = "WatchlistSettings";
+
+    // Settings xml will be stored in /data/system/network_watchlist/watchlist_settings.xml
+    static final String SYSTEM_WATCHLIST_DIR = "network_watchlist";
+
+    private static final String WATCHLIST_XML_FILE = "watchlist_settings.xml";
+
+    private static class XmlTags {
+        private static final String WATCHLIST_SETTINGS = "watchlist-settings";
+        private static final String SHA256_DOMAIN = "sha256-domain";
+        private static final String CRC32_DOMAIN = "crc32-domain";
+        private static final String SHA256_IP = "sha256-ip";
+        private static final String CRC32_IP = "crc32-ip";
+        private static final String HASH = "hash";
+    }
+
+    private static WatchlistSettings sInstance = new WatchlistSettings();
+    private final AtomicFile mXmlFile;
+    private final Object mLock = new Object();
+    private HarmfulDigests mCrc32DomainDigests = new HarmfulDigests(new ArrayList<>());
+    private HarmfulDigests mSha256DomainDigests = new HarmfulDigests(new ArrayList<>());
+    private HarmfulDigests mCrc32IpDigests = new HarmfulDigests(new ArrayList<>());
+    private HarmfulDigests mSha256IpDigests = new HarmfulDigests(new ArrayList<>());
+
+    public static synchronized WatchlistSettings getInstance() {
+        return sInstance;
+    }
+
+    private WatchlistSettings() {
+        this(getSystemWatchlistFile(WATCHLIST_XML_FILE));
+    }
+
+    @VisibleForTesting
+    protected WatchlistSettings(File xmlFile) {
+        mXmlFile = new AtomicFile(xmlFile);
+        readSettingsLocked();
+    }
+
+    static File getSystemWatchlistFile(String filename) {
+        final File dataSystemDir = Environment.getDataSystemDirectory();
+        final File systemWatchlistDir = new File(dataSystemDir, SYSTEM_WATCHLIST_DIR);
+        systemWatchlistDir.mkdirs();
+        return new File(systemWatchlistDir, filename);
+    }
+
+    private void readSettingsLocked() {
+        synchronized (mLock) {
+            FileInputStream stream;
+            try {
+                stream = mXmlFile.openRead();
+            } catch (FileNotFoundException e) {
+                Log.i(TAG, "No watchlist settings: " + mXmlFile.getBaseFile().getAbsolutePath());
+                return;
+            }
+
+            final List<byte[]> crc32DomainList = new ArrayList<>();
+            final List<byte[]> sha256DomainList = new ArrayList<>();
+            final List<byte[]> crc32IpList = new ArrayList<>();
+            final List<byte[]> sha256IpList = new ArrayList<>();
+
+            try {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(stream, StandardCharsets.UTF_8.name());
+                parser.nextTag();
+                parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+                while (parser.nextTag() == XmlPullParser.START_TAG) {
+                    String tagName = parser.getName();
+                    switch (tagName) {
+                        case XmlTags.CRC32_DOMAIN:
+                            parseHash(parser, tagName, crc32DomainList);
+                            break;
+                        case XmlTags.CRC32_IP:
+                            parseHash(parser, tagName, crc32IpList);
+                            break;
+                        case XmlTags.SHA256_DOMAIN:
+                            parseHash(parser, tagName, sha256DomainList);
+                            break;
+                        case XmlTags.SHA256_IP:
+                            parseHash(parser, tagName, sha256IpList);
+                            break;
+                        default:
+                            Log.w(TAG, "Unknown element: " + parser.getName());
+                            XmlUtils.skipCurrentTag(parser);
+                    }
+                }
+                parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+                writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList);
+            } catch (IllegalStateException | NullPointerException | NumberFormatException |
+                    XmlPullParserException | IOException | IndexOutOfBoundsException e) {
+                Log.w(TAG, "Failed parsing " + e);
+            } finally {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    private void parseHash(XmlPullParser parser, String tagName, List<byte[]> hashSet)
+            throws IOException, XmlPullParserException {
+        parser.require(XmlPullParser.START_TAG, null, tagName);
+        while (parser.nextTag() == XmlPullParser.START_TAG) {
+            parser.require(XmlPullParser.START_TAG, null, XmlTags.HASH);
+            byte[] hash = HexDump.hexStringToByteArray(parser.nextText());
+            parser.require(XmlPullParser.END_TAG, null, XmlTags.HASH);
+            hashSet.add(hash);
+        }
+        parser.require(XmlPullParser.END_TAG, null, tagName);
+    }
+
+    /**
+     * Write network watchlist settings to disk.
+     * Adb should not use it, should use writeSettingsToMemory directly instead.
+     */
+    public void writeSettingsToDisk(List<byte[]> newCrc32DomainList,
+            List<byte[]> newSha256DomainList,
+            List<byte[]> newCrc32IpList,
+            List<byte[]> newSha256IpList) {
+        synchronized (mLock) {
+            FileOutputStream stream;
+            try {
+                stream = mXmlFile.startWrite();
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to write display settings: " + e);
+                return;
+            }
+
+            try {
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(stream, StandardCharsets.UTF_8.name());
+                out.startDocument(null, true);
+                out.startTag(null, XmlTags.WATCHLIST_SETTINGS);
+
+                writeHashSetToXml(out, XmlTags.SHA256_DOMAIN, newSha256DomainList);
+                writeHashSetToXml(out, XmlTags.SHA256_IP, newSha256IpList);
+                writeHashSetToXml(out, XmlTags.CRC32_DOMAIN, newCrc32DomainList);
+                writeHashSetToXml(out, XmlTags.CRC32_IP, newCrc32IpList);
+
+                out.endTag(null, XmlTags.WATCHLIST_SETTINGS);
+                out.endDocument();
+                mXmlFile.finishWrite(stream);
+                writeSettingsToMemory(newCrc32DomainList, newSha256DomainList, newCrc32IpList,
+                        newSha256IpList);
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to write display settings, restoring backup.", e);
+                mXmlFile.failWrite(stream);
+            }
+        }
+    }
+
+    /**
+     * Write network watchlist settings to memory.
+     */
+    public void writeSettingsToMemory(List<byte[]> newCrc32DomainList,
+            List<byte[]> newSha256DomainList,
+            List<byte[]> newCrc32IpList,
+            List<byte[]> newSha256IpList) {
+        synchronized (mLock) {
+            mCrc32DomainDigests = new HarmfulDigests(newCrc32DomainList);
+            mCrc32IpDigests = new HarmfulDigests(newCrc32IpList);
+            mSha256DomainDigests = new HarmfulDigests(newSha256DomainList);
+            mSha256IpDigests = new HarmfulDigests(newSha256IpList);
+        }
+    }
+
+    private static void writeHashSetToXml(XmlSerializer out, String tagName, List<byte[]> hashSet)
+            throws IOException {
+        out.startTag(null, tagName);
+        for (byte[] hash : hashSet) {
+            out.startTag(null, XmlTags.HASH);
+            out.text(HexDump.toHexString(hash));
+            out.endTag(null, XmlTags.HASH);
+        }
+        out.endTag(null, tagName);
+    }
+
+    public boolean containsDomain(String domain) {
+        // First it does a quick CRC32 check.
+        final byte[] crc32 = getCrc32(domain);
+        if (!mCrc32DomainDigests.contains(crc32)) {
+            return false;
+        }
+        // Now we do a slow SHA256 check.
+        final byte[] sha256 = getSha256(domain);
+        return mSha256DomainDigests.contains(sha256);
+    }
+
+    public boolean containsIp(String ip) {
+        // First it does a quick CRC32 check.
+        final byte[] crc32 = getCrc32(ip);
+        if (!mCrc32IpDigests.contains(crc32)) {
+            return false;
+        }
+        // Now we do a slow SHA256 check.
+        final byte[] sha256 = getSha256(ip);
+        return mSha256IpDigests.contains(sha256);
+    }
+
+
+    /** Get CRC32 of a string */
+    private byte[] getCrc32(String str) {
+        final CRC32 crc = new CRC32();
+        crc.update(str.getBytes());
+        final long tmp = crc.getValue();
+        return new byte[]{(byte)(tmp >> 24 & 255), (byte)(tmp >> 16 & 255),
+                (byte)(tmp >> 8 & 255), (byte)(tmp & 255)};
+    }
+
+    /** Get SHA256 of a string */
+    private byte[] getSha256(String str) {
+        MessageDigest messageDigest;
+        try {
+            messageDigest = MessageDigest.getInstance("SHA256");
+        } catch (NoSuchAlgorithmException e) {
+            /* can't happen */
+            return null;
+        }
+        messageDigest.update(str.getBytes());
+        return messageDigest.digest();
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("Domain CRC32 digest list:");
+        mCrc32DomainDigests.dump(fd, pw, args);
+        pw.println("Domain SHA256 digest list:");
+        mSha256DomainDigests.dump(fd, pw, args);
+        pw.println("Ip CRC32 digest list:");
+        mCrc32IpDigests.dump(fd, pw, args);
+        pw.println("Ip SHA256 digest list:");
+        mSha256IpDigests.dump(fd, pw, args);
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f61cec9..1e9fab5 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -835,7 +835,8 @@
         // alarm restrictions
         final boolean muteMediaAndSystemSounds = zen && !mConfig.allowMediaSystemOther;
         // total silence restrictions
-        final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+        final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
+                || areAllBehaviorSoundsMuted();
 
         for (int usage : AudioAttributes.SDK_USAGES) {
             final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
@@ -855,9 +856,11 @@
         }
     }
 
+
     @VisibleForTesting
     protected void applyRestrictions(boolean mute, int usage) {
         final String[] exceptionPackages = null; // none (for now)
+
         mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage,
                 mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
                 exceptionPackages);
@@ -866,6 +869,12 @@
                 exceptionPackages);
     }
 
+    private boolean areAllBehaviorSoundsMuted() {
+        return !mConfig.allowAlarms  && !mConfig.allowMediaSystemOther && !mConfig.allowReminders
+                && !mConfig.allowCalls && !mConfig.allowMessages && !mConfig.allowEvents
+                && !mConfig.allowRepeatCallers;
+    }
+
     private void applyZenToRingerMode() {
         if (mAudioManager == null) return;
         // force the ringer mode into compliance
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 50ac409..29f48ee 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -110,9 +110,9 @@
             return false;
         }
 
-        // We do not dexopt a priv-app package when pm.dexopt.priv-apps is false.
+        // We do not dexopt a priv-app package when pm.dexopt.priv-apps-oob is true.
         if (pkg.isPrivileged()) {
-            return SystemProperties.getBoolean("pm.dexopt.priv-apps", true);
+            return !SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false);
         }
 
         return true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6a0ea4ee..83cffe5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -551,6 +551,8 @@
 
     private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
 
+    private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob";
+
     /** Canonical intent used to identify what counts as a "web browser" app */
     private static final Intent sBrowserIntent;
     static {
@@ -9598,7 +9600,7 @@
 
         if (Build.IS_DEBUGGABLE &&
                 pkg.isPrivileged() &&
-                !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+                SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) {
             PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
         }
 
@@ -19900,6 +19902,23 @@
                         .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
         co.onChange(true);
 
+        // This observer provides an one directional mapping from Global.PRIV_APP_OOB_ENABLED to
+        // pm.dexopt.priv-apps-oob property. This is only for experiment and should be removed once
+        // it is done.
+        ContentObserver privAppOobObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                int oobEnabled = Global.getInt(resolver, Global.PRIV_APP_OOB_ENABLED, 0);
+                SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB,
+                        oobEnabled == 1 ? "true" : "false");
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(
+                Global.getUriFor(Global.PRIV_APP_OOB_ENABLED), false, privAppOobObserver,
+                UserHandle.USER_SYSTEM);
+        // At boot, restore the value from the setting, which persists across reboot.
+        privAppOobObserver.onChange(true);
+
         // Disable any carrier apps. We do this very early in boot to prevent the apps from being
         // disabled after already being started.
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 781216c..19b0d9b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -52,18 +52,7 @@
     // Load the property for the given reason and check for validity. This will throw an
     // exception in case the reason or value are invalid.
     private static String getAndCheckValidity(int reason) {
-        String sysPropName = getSystemPropertyName(reason);
-        String sysPropValue;
-        // TODO: This is a temporary hack to keep marlin booting on aosp/master while we
-        // figure out how to deal with these system properties that currently appear on
-        // vendor.
-        if ("pm.dexopt.inactive".equals(sysPropName)) {
-            sysPropValue = "verify";
-        } else if ("pm.dexopt.shared".equals(sysPropName)) {
-            sysPropValue = "speed";
-        } else {
-            sysPropValue = SystemProperties.get(sysPropName);
-        }
+        String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
         if (sysPropValue == null || sysPropValue.isEmpty() ||
                 !DexFile.isValidCompilerFilter(sysPropValue)) {
             throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1e5245c..1152310 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1010,6 +1010,23 @@
         }
     }
 
+    @Override
+    public boolean hasRestrictedProfiles() {
+        checkManageUsersPermission("hasRestrictedProfiles");
+        final int callingUserId = UserHandle.getCallingUserId();
+        synchronized (mUsersLock) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
+                UserInfo profile = mUsers.valueAt(i).info;
+                if (callingUserId != profile.id
+                        && profile.restrictedProfileParentId == callingUserId) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
     /*
      * Should be locked on mUsers before calling this.
      */
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c86122f..4b13404 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -66,6 +66,7 @@
 
     public static final Set<String> USER_RESTRICTIONS = newSetWithUniqueCheck(new String[] {
             UserManager.DISALLOW_CONFIG_WIFI,
+            UserManager.DISALLOW_CONFIG_LOCALE,
             UserManager.DISALLOW_MODIFY_ACCOUNTS,
             UserManager.DISALLOW_INSTALL_APPS,
             UserManager.DISALLOW_UNINSTALL_APPS,
@@ -112,6 +113,7 @@
             UserManager.DISALLOW_OEM_UNLOCK,
             UserManager.DISALLOW_UNMUTE_DEVICE,
             UserManager.DISALLOW_AUTOFILL,
+            UserManager.DISALLOW_USER_SWITCH
     });
 
     /**
@@ -143,6 +145,13 @@
     );
 
     /**
+     * User restrictions that cannot be set by profile owners. Applied to all users.
+     */
+    private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
+            UserManager.DISALLOW_USER_SWITCH
+    );
+
+    /**
      * User restrictions that can't be changed by device owner or profile owner.
      */
     private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet(
@@ -311,6 +320,7 @@
      */
     public static boolean canProfileOwnerChange(String restriction, int userId) {
         return !IMMUTABLE_BY_OWNERS.contains(restriction)
+                && !DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)
                 && !(userId != UserHandle.USER_SYSTEM
                     && PRIMARY_USER_ONLY_RESTRICTIONS.contains(restriction));
     }
@@ -363,8 +373,9 @@
      */
     private static boolean isGlobal(boolean isDeviceOwner, String key) {
         return (isDeviceOwner &&
-                (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key)|| GLOBAL_RESTRICTIONS.contains(key)))
-                || PROFILE_GLOBAL_RESTRICTIONS.contains(key);
+                (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)))
+                || PROFILE_GLOBAL_RESTRICTIONS.contains(key)
+                || DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java
new file mode 100644
index 0000000..0913269
--- /dev/null
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java
@@ -0,0 +1,34 @@
+/*
+ * 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.pm.crossprofile;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+public class CrossProfileAppsService extends SystemService {
+    private CrossProfileAppsServiceImpl mServiceImpl;
+
+    public CrossProfileAppsService(Context context) {
+        super(context);
+        mServiceImpl = new CrossProfileAppsServiceImpl(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.CROSS_PROFILE_APPS_SERVICE, mServiceImpl);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
new file mode 100644
index 0000000..854b704
--- /dev/null
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
@@ -0,0 +1,264 @@
+/*
+ * 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.pm.crossprofile;
+
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.crossprofile.ICrossProfileApps;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
+    private static final String TAG = "CrossProfileAppsService";
+
+    private Context mContext;
+    private Injector mInjector;
+
+    public CrossProfileAppsServiceImpl(Context context) {
+        this(context, new InjectorImpl(context));
+    }
+
+    @VisibleForTesting
+    CrossProfileAppsServiceImpl(Context context, Injector injector) {
+        mContext = context;
+        mInjector = injector;
+    }
+
+    @Override
+    public List<UserHandle> getTargetUserProfiles(String callingPackage) {
+        Preconditions.checkNotNull(callingPackage);
+
+        verifyCallingPackage(callingPackage);
+
+        return getTargetUserProfilesUnchecked(
+                callingPackage, mInjector.getCallingUserId());
+    }
+
+    @Override
+    public void startActivityAsUser(
+            String callingPackage,
+            ComponentName component,
+            Rect sourceBounds,
+            Bundle startActivityOptions,
+            UserHandle user) throws RemoteException {
+        Preconditions.checkNotNull(callingPackage);
+        Preconditions.checkNotNull(component);
+        Preconditions.checkNotNull(user);
+
+        verifyCallingPackage(callingPackage);
+
+        List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
+                callingPackage, mInjector.getCallingUserId());
+        if (!allowedTargetUsers.contains(user)) {
+            throw new SecurityException(
+                    callingPackage + " cannot access unrelated user " + user.getIdentifier());
+        }
+
+        // Verify that caller package is starting activity in its own package.
+        if (!callingPackage.equals(component.getPackageName())) {
+            throw new SecurityException(
+                    callingPackage + " attempts to start an activity in other package - "
+                            + component.getPackageName());
+        }
+
+        final int callingUid = mInjector.getCallingUid();
+
+        // Verify that target activity does handle the intent with ACTION_MAIN and
+        // CATEGORY_LAUNCHER as calling startActivityAsUser ignore them if component is present.
+        final Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        launchIntent.setSourceBounds(sourceBounds);
+        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        // Only package name is set here, as opposed to component name, because intent action and
+        // category are ignored if component name is present while we are resolving intent.
+        launchIntent.setPackage(component.getPackageName());
+        verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, user);
+
+        final long ident = mInjector.clearCallingIdentity();
+        try {
+            launchIntent.setComponent(component);
+            mContext.startActivityAsUser(launchIntent, startActivityOptions, user);
+        } finally {
+            mInjector.restoreCallingIdentity(ident);
+        }
+    }
+
+    private List<UserHandle> getTargetUserProfilesUnchecked(
+            String callingPackage, @UserIdInt int callingUserId) {
+        final long ident = mInjector.clearCallingIdentity();
+        try {
+            final int[] enabledProfileIds =
+                    mInjector.getUserManager().getEnabledProfileIds(callingUserId);
+
+            List<UserHandle> targetProfiles = new ArrayList<>();
+            for (final int userId : enabledProfileIds) {
+                if (userId == callingUserId) {
+                    continue;
+                }
+                if (!isPackageEnabled(callingPackage, userId)) {
+                    continue;
+                }
+                targetProfiles.add(UserHandle.of(userId));
+            }
+            return targetProfiles;
+        } finally {
+            mInjector.restoreCallingIdentity(ident);
+        }
+    }
+
+    private boolean isPackageEnabled(String packageName, @UserIdInt int userId) {
+        final int callingUid = mInjector.getCallingUid();
+        final long ident = mInjector.clearCallingIdentity();
+        try {
+            final PackageInfo info = mInjector.getPackageManagerInternal()
+                    .getPackageInfo(
+                            packageName,
+                            MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                            callingUid,
+                            userId);
+            return info != null && info.applicationInfo.enabled;
+        } finally {
+            mInjector.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Verify that the specified intent does resolved to the specified component and the resolved
+     * activity is exported.
+     */
+    private void verifyActivityCanHandleIntentAndExported(
+            Intent launchIntent, ComponentName component, int callingUid, UserHandle user) {
+        final long ident = mInjector.clearCallingIdentity();
+        try {
+            final List<ResolveInfo> apps =
+                    mInjector.getPackageManagerInternal().queryIntentActivities(
+                            launchIntent,
+                            MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                            callingUid,
+                            user.getIdentifier());
+            final int size = apps.size();
+            for (int i = 0; i < size; ++i) {
+                final ActivityInfo activityInfo = apps.get(i).activityInfo;
+                if (TextUtils.equals(activityInfo.packageName, component.getPackageName())
+                        && TextUtils.equals(activityInfo.name, component.getClassName())
+                        && activityInfo.exported) {
+                    return;
+                }
+            }
+            throw new SecurityException("Attempt to launch activity without "
+                    + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component);
+        } finally {
+            mInjector.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Verify that the given calling package is belong to the calling UID.
+     */
+    private void verifyCallingPackage(String callingPackage) {
+        mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage);
+    }
+
+    private static class InjectorImpl implements Injector {
+        private Context mContext;
+
+        public InjectorImpl(Context context) {
+            mContext = context;
+        }
+
+        public int getCallingUid() {
+            return Binder.getCallingUid();
+        }
+
+        public int getCallingUserId() {
+            return UserHandle.getCallingUserId();
+        }
+
+        public UserHandle getCallingUserHandle() {
+            return Binder.getCallingUserHandle();
+        }
+
+        public long clearCallingIdentity() {
+            return Binder.clearCallingIdentity();
+        }
+
+        public void restoreCallingIdentity(long token) {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        public UserManager getUserManager() {
+            return mContext.getSystemService(UserManager.class);
+        }
+
+        public PackageManagerInternal getPackageManagerInternal() {
+            return LocalServices.getService(PackageManagerInternal.class);
+        }
+
+        public PackageManager getPackageManager() {
+            return mContext.getPackageManager();
+        }
+
+        public AppOpsManager getAppOpsManager() {
+            return mContext.getSystemService(AppOpsManager.class);
+        }
+    }
+
+    @VisibleForTesting
+    public interface Injector {
+        int getCallingUid();
+
+        int getCallingUserId();
+
+        UserHandle getCallingUserHandle();
+
+        long clearCallingIdentity();
+
+        void restoreCallingIdentity(long token);
+
+        UserManager getUserManager();
+
+        PackageManagerInternal getPackageManagerInternal();
+
+        PackageManager getPackageManager();
+
+        AppOpsManager getAppOpsManager();
+
+    }
+}
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 7a2e630..3707a5e 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -58,7 +58,7 @@
 
     public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
         if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
-        if (mStatusBarInternal.isGlobalActionsDisabled()) {
+        if (mStatusBarInternal != null && mStatusBarInternal.isGlobalActionsDisabled()) {
             return;
         }
         mKeyguardShowing = keyguardShowing;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 936ffdf..9162a97 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -59,6 +59,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
@@ -66,6 +68,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
@@ -123,11 +126,8 @@
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 
-import static com.android.server.wm.proto.WindowManagerPolicyProto.STABLE_BOUNDS;
-
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityThread;
@@ -210,6 +210,7 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayFrames;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -596,47 +597,6 @@
 
     PointerLocationView mPointerLocationView;
 
-    // The current size of the screen; really; extends into the overscan area of
-    // the screen and doesn't account for any system elements like the status bar.
-    int mOverscanScreenLeft, mOverscanScreenTop;
-    int mOverscanScreenWidth, mOverscanScreenHeight;
-    // The current visible size of the screen; really; (ir)regardless of whether the status
-    // bar can be hidden but not extending into the overscan area.
-    int mUnrestrictedScreenLeft, mUnrestrictedScreenTop;
-    int mUnrestrictedScreenWidth, mUnrestrictedScreenHeight;
-    // Like mOverscanScreen*, but allowed to move into the overscan region where appropriate.
-    int mRestrictedOverscanScreenLeft, mRestrictedOverscanScreenTop;
-    int mRestrictedOverscanScreenWidth, mRestrictedOverscanScreenHeight;
-    // The current size of the screen; these may be different than (0,0)-(dw,dh)
-    // if the status bar can't be hidden; in that case it effectively carves out
-    // that area of the display from all other windows.
-    int mRestrictedScreenLeft, mRestrictedScreenTop;
-    int mRestrictedScreenWidth, mRestrictedScreenHeight;
-    // During layout, the current screen borders accounting for any currently
-    // visible system UI elements.
-    int mSystemLeft, mSystemTop, mSystemRight, mSystemBottom;
-    // For applications requesting stable content insets, these are them.
-    int mStableLeft, mStableTop, mStableRight, mStableBottom;
-    // For applications requesting stable content insets but have also set the
-    // fullscreen window flag, these are the stable dimensions without the status bar.
-    int mStableFullscreenLeft, mStableFullscreenTop;
-    int mStableFullscreenRight, mStableFullscreenBottom;
-    // During layout, the current screen borders with all outer decoration
-    // (status bar, input method dock) accounted for.
-    int mCurLeft, mCurTop, mCurRight, mCurBottom;
-    // During layout, the frame in which content should be displayed
-    // to the user, accounting for all screen decoration except for any
-    // space they deem as available for other content.  This is usually
-    // the same as mCur*, but may be larger if the screen decor has supplied
-    // content insets.
-    int mContentLeft, mContentTop, mContentRight, mContentBottom;
-    // During layout, the frame in which voice content should be displayed
-    // to the user, accounting for all screen decoration except for any
-    // space they deem as available for other content.
-    int mVoiceContentLeft, mVoiceContentTop, mVoiceContentRight, mVoiceContentBottom;
-    // During layout, the current screen borders along which input method
-    // windows are placed.
-    int mDockLeft, mDockTop, mDockRight, mDockBottom;
     // During layout, the layer at which the doc window is placed.
     int mDockLayer;
     // During layout, this is the layer of the status bar.
@@ -734,18 +694,11 @@
 
     Display mDisplay;
 
-    private int mDisplayRotation;
-
     int mLandscapeRotation = 0;  // default landscape rotation
     int mSeascapeRotation = 0;   // "other" landscape rotation, 180 degrees from mLandscapeRotation
     int mPortraitRotation = 0;   // default portrait rotation
     int mUpsideDownRotation = 0; // "other" portrait rotation
 
-    int mOverscanLeft = 0;
-    int mOverscanTop = 0;
-    int mOverscanRight = 0;
-    int mOverscanBottom = 0;
-
     // What we do when the user long presses on home
     private int mLongPressOnHomeBehavior;
 
@@ -1061,7 +1014,7 @@
             View.NAVIGATION_BAR_UNHIDE,
             View.NAVIGATION_BAR_TRANSLUCENT,
             StatusBarManager.WINDOW_NAVIGATION_BAR,
-            WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
+            FLAG_TRANSLUCENT_NAVIGATION,
             View.NAVIGATION_BAR_TRANSPARENT);
 
     private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
@@ -1172,14 +1125,12 @@
                 + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled
                 + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
                 + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
-        final boolean keyguardGoingAway = mWindowManagerInternal.isKeyguardGoingAway();
 
         boolean disable = true;
         // Note: We postpone the rotating of the screen until the keyguard as well as the
         // window manager have reported a draw complete or the keyguard is going away in dismiss
         // mode.
-        if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete)
-                || keyguardGoingAway)) {
+        if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete))) {
             if (needSensorRunningLp()) {
                 disable = false;
                 //enable listener if not already enabled
@@ -1190,7 +1141,7 @@
                     // the sensor reading was cleared which can cause it to relaunch the app that
                     // will show in the wrong orientation first before correcting leading to app
                     // launch delays.
-                    mOrientationListener.enable(!keyguardGoingAway /* clearCurrentRotation */);
+                    mOrientationListener.enable(true /* clearCurrentRotation */);
                     if(localLOGV) Slog.v(TAG, "Enabling listeners");
                     mOrientationSensorEnabled = true;
                 }
@@ -2070,6 +2021,7 @@
         context.registerReceiver(mMultiuserReceiver, filter);
 
         // monitor for system gestures
+        // TODO(multi-display): Needs to be display specific.
         mSystemGestures = new SystemGesturesPointerEventListener(context,
                 new SystemGesturesPointerEventListener.Callbacks() {
                     @Override
@@ -2316,17 +2268,6 @@
         return mForceDefaultOrientation;
     }
 
-    @Override
-    public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) {
-        // TODO(multi-display): Define policy for secondary displays.
-        if (display.getDisplayId() == DEFAULT_DISPLAY) {
-            mOverscanLeft = left;
-            mOverscanTop = top;
-            mOverscanRight = right;
-            mOverscanBottom = bottom;
-        }
-    }
-
     public void updateSettings() {
         ContentResolver resolver = mContext.getContentResolver();
         boolean updateRotation = false;
@@ -4323,12 +4264,16 @@
     }
 
     @Override
+    // TODO: Should probably be moved into DisplayFrames.
     public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
-            int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
-            Rect outStableInsets, Rect outOutsets) {
+            DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
+            Rect outOutsets) {
         final int fl = PolicyControl.getWindowFlags(null, attrs);
         final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
         final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
+        final int displayRotation = displayFrames.mRotation;
+        final int displayWidth = displayFrames.mDisplayWidth;
+        final int displayHeight = displayFrames.mDisplayHeight;
 
         final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
         if (useOutsets) {
@@ -4351,34 +4296,33 @@
             int availRight, availBottom;
             if (canHideNavigationBar() &&
                     (systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
-                availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
-                availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                availRight = displayFrames.mUnrestricted.right;
+                availBottom = displayFrames.mUnrestricted.bottom;
             } else {
-                availRight = mRestrictedScreenLeft + mRestrictedScreenWidth;
-                availBottom = mRestrictedScreenTop + mRestrictedScreenHeight;
+                availRight = displayFrames.mRestricted.right;
+                availBottom = displayFrames.mRestricted.bottom;
             }
+            outStableInsets.set(displayFrames.mStable.left, displayFrames.mStable.top,
+                    availRight - displayFrames.mStable.right,
+                    availBottom - displayFrames.mStable.bottom);
+
             if ((systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
                 if ((fl & FLAG_FULLSCREEN) != 0) {
-                    outContentInsets.set(mStableFullscreenLeft, mStableFullscreenTop,
-                            availRight - mStableFullscreenRight,
-                            availBottom - mStableFullscreenBottom);
+                    outContentInsets.set(displayFrames.mStableFullscreen.left,
+                            displayFrames.mStableFullscreen.top,
+                            availRight - displayFrames.mStableFullscreen.right,
+                            availBottom - displayFrames.mStableFullscreen.bottom);
                 } else {
-                    outContentInsets.set(mStableLeft, mStableTop,
-                            availRight - mStableRight, availBottom - mStableBottom);
+                    outContentInsets.set(outStableInsets);
                 }
             } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
                 outContentInsets.setEmpty();
-            } else if ((systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
-                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 0) {
-                outContentInsets.set(mCurLeft, mCurTop,
-                        availRight - mCurRight, availBottom - mCurBottom);
             } else {
-                outContentInsets.set(mCurLeft, mCurTop,
-                        availRight - mCurRight, availBottom - mCurBottom);
+                outContentInsets.set(displayFrames.mCurrent.left, displayFrames.mCurrent.top,
+                        availRight - displayFrames.mCurrent.right,
+                        availBottom - displayFrames.mCurrent.bottom);
             }
 
-            outStableInsets.set(mStableLeft, mStableTop,
-                    availRight - mStableRight, availBottom - mStableBottom);
             if (taskBounds != null) {
                 calculateRelevantTaskInsets(taskBounds, outContentInsets,
                         displayWidth, displayHeight);
@@ -4415,68 +4359,11 @@
 
     /** {@inheritDoc} */
     @Override
-    public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
-            int displayRotation, int uiMode) {
-        final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY;
-        mDisplayRotation = displayRotation;
-        final int overscanLeft, overscanTop, overscanRight, overscanBottom;
-        if (isDefaultDisplay) {
-            switch (displayRotation) {
-                case Surface.ROTATION_90:
-                    overscanLeft = mOverscanTop;
-                    overscanTop = mOverscanRight;
-                    overscanRight = mOverscanBottom;
-                    overscanBottom = mOverscanLeft;
-                    break;
-                case Surface.ROTATION_180:
-                    overscanLeft = mOverscanRight;
-                    overscanTop = mOverscanBottom;
-                    overscanRight = mOverscanLeft;
-                    overscanBottom = mOverscanTop;
-                    break;
-                case Surface.ROTATION_270:
-                    overscanLeft = mOverscanBottom;
-                    overscanTop = mOverscanLeft;
-                    overscanRight = mOverscanTop;
-                    overscanBottom = mOverscanRight;
-                    break;
-                default:
-                    overscanLeft = mOverscanLeft;
-                    overscanTop = mOverscanTop;
-                    overscanRight = mOverscanRight;
-                    overscanBottom = mOverscanBottom;
-                    break;
-            }
-        } else {
-            overscanLeft = 0;
-            overscanTop = 0;
-            overscanRight = 0;
-            overscanBottom = 0;
-        }
-        mOverscanScreenLeft = mRestrictedOverscanScreenLeft = 0;
-        mOverscanScreenTop = mRestrictedOverscanScreenTop = 0;
-        mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
-        mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
-        mSystemLeft = 0;
-        mSystemTop = 0;
-        mSystemRight = displayWidth;
-        mSystemBottom = displayHeight;
-        mUnrestrictedScreenLeft = overscanLeft;
-        mUnrestrictedScreenTop = overscanTop;
-        mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight;
-        mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
-        mRestrictedScreenLeft = mUnrestrictedScreenLeft;
-        mRestrictedScreenTop = mUnrestrictedScreenTop;
-        mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
-        mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
-        mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft
-                = mCurLeft = mUnrestrictedScreenLeft;
-        mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop
-                = mCurTop = mUnrestrictedScreenTop;
-        mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight
-                = mCurRight = displayWidth - overscanRight;
-        mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom
-                = mCurBottom = displayHeight - overscanBottom;
+    public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
+        displayFrames.onBeginLayout();
+        // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
+        mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
+        mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
         mDockLayer = 0x10000000;
         mStatusBarLayer = -1;
 
@@ -4486,13 +4373,13 @@
         final Rect of = mTmpOverscanFrame;
         final Rect vf = mTmpVisibleFrame;
         final Rect dcf = mTmpDecorFrame;
-        pf.left = df.left = of.left = vf.left = mDockLeft;
-        pf.top = df.top = of.top = vf.top = mDockTop;
-        pf.right = df.right = of.right = vf.right = mDockRight;
-        pf.bottom = df.bottom = of.bottom = vf.bottom = mDockBottom;
+        vf.set(displayFrames.mDock);
+        of.set(displayFrames.mDock);
+        df.set(displayFrames.mDock);
+        pf.set(displayFrames.mDock);
         dcf.setEmpty();  // Decor frame N/A for system bars.
 
-        if (isDefaultDisplay) {
+        if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
             // For purposes of putting out fake window up to steal focus, we will
             // drive nav being hidden only by whether it is requested.
             final int sysui = mLastSystemUiFlags;
@@ -4511,10 +4398,9 @@
                     && mStatusBar.getAttrs().height == MATCH_PARENT
                     && mStatusBar.getAttrs().width == MATCH_PARENT;
 
-            // When the navigation bar isn't visible, we put up a fake
-            // input window to catch all touch events.  This way we can
-            // detect when the user presses anywhere to bring back the nav
-            // bar and ensure the application doesn't see the event.
+            // When the navigation bar isn't visible, we put up a fake input window to catch all
+            // touch events. This way we can detect when the user presses anywhere to bring back the
+            // nav bar and ensure the application doesn't see the event.
             if (navVisible || navAllowedHidden) {
                 if (mInputConsumer != null) {
                     mHandler.sendMessage(
@@ -4530,30 +4416,32 @@
                 InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
             }
 
-            // For purposes of positioning and showing the nav bar, if we have
-            // decided that it can't be hidden (because of the screen aspect ratio),
-            // then take that into account.
+            // For purposes of positioning and showing the nav bar, if we have decided that it can't
+            // be hidden (because of the screen aspect ratio), then take that into account.
             navVisible |= !canHideNavigationBar();
 
-            boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
-                    displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
-                    navAllowedHidden, statusBarExpandedNotKeyguard);
-            if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
-                    mDockLeft, mDockTop, mDockRight, mDockBottom));
-            updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);
+            boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
+                    navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
+            if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
+            updateSysUiVisibility |= layoutStatusBar(
+                    displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);
             if (updateSysUiVisibility) {
                 updateSystemUiVisibilityLw();
             }
         }
-        layoutScreenDecorWindows(displayId, displayWidth, displayHeight, pf, df, dcf);
+        layoutScreenDecorWindows(displayFrames, pf, df, dcf);
     }
 
-    private void layoutScreenDecorWindows(int displayId, int displayWidth, int displayHeight,
-            Rect pf, Rect df, Rect dcf) {
+    private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) {
         if (mScreenDecorWindows.isEmpty()) {
             return;
         }
 
+        final int displayId = displayFrames.mDisplayId;
+        final Rect dockFrame = displayFrames.mDock;
+        final int displayHeight = displayFrames.mDisplayHeight;
+        final int displayWidth = displayFrames.mDisplayWidth;
+
         for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
             final WindowState w = mScreenDecorWindows.valueAt(i);
             if (w.getDisplayId() != displayId || !w.isVisibleLw()) {
@@ -4570,10 +4458,10 @@
                 // Docked at left or top.
                 if (frame.bottom >= displayHeight) {
                     // Docked left.
-                    mDockLeft = Math.max(frame.right, mDockLeft);
+                    dockFrame.left = Math.max(frame.right, dockFrame.left);
                 } else if (frame.right >= displayWidth ) {
                     // Docked top.
-                    mDockTop = Math.max(frame.bottom, mDockTop);
+                    dockFrame.top = Math.max(frame.bottom, dockFrame.top);
                 } else {
                     Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
                             + " not docked on left or top of display. frame=" + frame
@@ -4583,10 +4471,10 @@
                 // Docked at right or bottom.
                 if (frame.top <= 0) {
                     // Docked right.
-                    mDockRight = Math.min(frame.left, mDockRight);
+                    dockFrame.right = Math.min(frame.left, dockFrame.right);
                 } else if (frame.left <= 0) {
                     // Docked bottom.
-                    mDockBottom = Math.min(frame.top, mDockBottom);
+                    dockFrame.bottom = Math.min(frame.top, dockFrame.bottom);
                 } else {
                     Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
                             + " not docked on right or bottom" + " of display. frame=" + frame
@@ -4600,194 +4488,165 @@
             }
         }
 
-        mContentTop = mSystemTop = mVoiceContentTop = mCurTop = mRestrictedScreenTop = mDockTop;
-        mContentLeft = mSystemLeft = mVoiceContentLeft = mCurLeft = mRestrictedScreenLeft
-                = mRestrictedOverscanScreenLeft = mDockLeft;
-        mContentBottom = mSystemBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
-        mContentRight = mSystemRight = mVoiceContentRight = mCurRight = mDockRight;
-
-        mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
-        mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
-        mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
-        mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
+        displayFrames.mRestricted.set(dockFrame);
+        displayFrames.mCurrent.set(dockFrame);
+        displayFrames.mVoiceContent.set(dockFrame);
+        displayFrames.mSystem.set(dockFrame);
+        displayFrames.mContent.set(dockFrame);
+        displayFrames.mRestrictedOverscan.set(dockFrame);
     }
 
-    private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
-            boolean isKeyguardShowing) {
+    private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,
+            Rect dcf, int sysui, boolean isKeyguardShowing) {
         // decide where the status bar goes ahead of time
-        if (mStatusBar != null) {
-            // apply any navigation bar insets
-            pf.left = df.left = of.left = mUnrestrictedScreenLeft;
-            pf.top = df.top = of.top = mUnrestrictedScreenTop;
-            pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
-            pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
-                    + mUnrestrictedScreenTop;
-            vf.left = mStableLeft;
-            vf.top = mStableTop;
-            vf.right = mStableRight;
-            vf.bottom = mStableBottom;
+        if (mStatusBar == null) {
+            return false;
+        }
+        // apply any navigation bar insets
+        of.set(displayFrames.mUnrestricted);
+        df.set(displayFrames.mUnrestricted);
+        pf.set(displayFrames.mUnrestricted);
+        vf.set(displayFrames.mStable);
 
-            mStatusBarLayer = mStatusBar.getSurfaceLayer();
+        mStatusBarLayer = mStatusBar.getSurfaceLayer();
 
-            // Let the status bar determine its size.
-            mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
-                    vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
-                    dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
+        // Let the status bar determine its size.
+        mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
+                vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
+                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
 
-            // For layout, the status bar is always at the top with our fixed height.
-            mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
+        // For layout, the status bar is always at the top with our fixed height.
+        displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
 
-            boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
-            boolean statusBarTranslucent = (sysui
-                    & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
-            if (!isKeyguardShowing) {
-                statusBarTranslucent &= areTranslucentBarsAllowed();
-            }
+        boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
+        boolean statusBarTranslucent = (sysui
+                & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
+        if (!isKeyguardShowing) {
+            statusBarTranslucent &= areTranslucentBarsAllowed();
+        }
 
-            // If the status bar is hidden, we don't want to cause
-            // windows behind it to scroll.
-            if (mStatusBar.isVisibleLw() && !statusBarTransient) {
-                // Status bar may go away, so the screen area it occupies
-                // is available to apps but just covering them when the
-                // status bar is visible.
-                mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
+        // If the status bar is hidden, we don't want to cause windows behind it to scroll.
+        if (mStatusBar.isVisibleLw() && !statusBarTransient) {
+            // Status bar may go away, so the screen area it occupies is available to apps but just
+            // covering them when the status bar is visible.
+            final Rect dockFrame = displayFrames.mDock;
+            dockFrame.top = displayFrames.mStable.top;
+            displayFrames.mContent.set(dockFrame);
+            displayFrames.mVoiceContent.set(dockFrame);
 
-                mContentTop = mVoiceContentTop = mCurTop = mDockTop;
-                mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
-                mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
-                mContentRight = mVoiceContentRight = mCurRight = mDockRight;
+            if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
+                    "dock=%s content=%s cur=%s", dockFrame.toString(),
+                    displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
 
-                if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
-                        String.format(
-                                "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
-                                mDockLeft, mDockTop, mDockRight, mDockBottom,
-                                mContentLeft, mContentTop, mContentRight, mContentBottom,
-                                mCurLeft, mCurTop, mCurRight, mCurBottom));
-            }
-            if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
-                    && !statusBarTransient && !statusBarTranslucent
+            if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
                     && !mStatusBarController.wasRecentlyTranslucent()) {
-                // If the opaque status bar is currently requested to be visible,
-                // and not in the process of animating on or off, then
-                // we can tell the app that it is covered by it.
-                mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
-            }
-            if (mStatusBarController.checkHiddenLw()) {
-                return true;
+                // If the opaque status bar is currently requested to be visible, and not in the
+                // process of animating on or off, then we can tell the app that it is covered by it.
+                displayFrames.mSystem.top = displayFrames.mStable.top;
             }
         }
-        return false;
+        return mStatusBarController.checkHiddenLw();
     }
 
-    private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
-            int uiMode, int overscanLeft, int overscanRight, int overscanBottom, Rect dcf,
+    private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
             boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
             boolean statusBarExpandedNotKeyguard) {
-        if (mNavigationBar != null) {
-            boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
-            // Force the navigation bar to its appropriate place and
-            // size.  We need to do this directly, instead of relying on
-            // it to bubble up from the nav bar, because this needs to
-            // change atomically with screen rotations.
-            mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight,
-                    displayRotation);
-            if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
-                // It's a system nav bar or a portrait screen; nav bar goes on bottom.
-                int top = displayHeight - overscanBottom
-                        - getNavigationBarHeight(displayRotation, uiMode);
-                mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
-                mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
-                if (transientNavBarShowing) {
-                    mNavigationBarController.setBarShowingLw(true);
-                } else if (navVisible) {
-                    mNavigationBarController.setBarShowingLw(true);
-                    mDockBottom = mTmpNavigationFrame.top;
-                    mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
-                    mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
-                } else {
-                    // We currently want to hide the navigation UI - unless we expanded the status
-                    // bar.
-                    mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
-                }
-                if (navVisible && !navTranslucent && !navAllowedHidden
-                        && !mNavigationBar.isAnimatingLw()
-                        && !mNavigationBarController.wasRecentlyTranslucent()) {
-                    // If the opaque nav bar is currently requested to be visible,
-                    // and not in the process of animating on or off, then
-                    // we can tell the app that it is covered by it.
-                    mSystemBottom = mTmpNavigationFrame.top;
-                }
-            } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
-                // Landscape screen; nav bar goes to the right.
-                int left = displayWidth - overscanRight
-                        - getNavigationBarWidth(displayRotation, uiMode);
-                mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
-                mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
-                if (transientNavBarShowing) {
-                    mNavigationBarController.setBarShowingLw(true);
-                } else if (navVisible) {
-                    mNavigationBarController.setBarShowingLw(true);
-                    mDockRight = mTmpNavigationFrame.left;
-                    mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
-                    mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
-                } else {
-                    // We currently want to hide the navigation UI - unless we expanded the status
-                    // bar.
-                    mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
-                }
-                if (navVisible && !navTranslucent && !navAllowedHidden
-                        && !mNavigationBar.isAnimatingLw()
-                        && !mNavigationBarController.wasRecentlyTranslucent()) {
-                    // If the nav bar is currently requested to be visible,
-                    // and not in the process of animating on or off, then
-                    // we can tell the app that it is covered by it.
-                    mSystemRight = mTmpNavigationFrame.left;
-                }
-            } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
-                // Seascape screen; nav bar goes to the left.
-                int right = overscanLeft + getNavigationBarWidth(displayRotation, uiMode);
-                mTmpNavigationFrame.set(overscanLeft, 0, right, displayHeight);
-                mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;
-                if (transientNavBarShowing) {
-                    mNavigationBarController.setBarShowingLw(true);
-                } else if (navVisible) {
-                    mNavigationBarController.setBarShowingLw(true);
-                    mDockLeft = mTmpNavigationFrame.right;
-                    // TODO: not so sure about those:
-                    mRestrictedScreenLeft = mRestrictedOverscanScreenLeft = mDockLeft;
-                    mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
-                    mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
-                } else {
-                    // We currently want to hide the navigation UI - unless we expanded the status
-                    // bar.
-                    mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
-                }
-                if (navVisible && !navTranslucent && !navAllowedHidden
-                        && !mNavigationBar.isAnimatingLw()
-                        && !mNavigationBarController.wasRecentlyTranslucent()) {
-                    // If the nav bar is currently requested to be visible,
-                    // and not in the process of animating on or off, then
-                    // we can tell the app that it is covered by it.
-                    mSystemLeft = mTmpNavigationFrame.right;
-                }
+        if (mNavigationBar == null) {
+            return false;
+        }
+        boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+        // Force the navigation bar to its appropriate place and size. We need to do this directly,
+        // instead of relying on it to bubble up from the nav bar, because this needs to change
+        // atomically with screen rotations.
+        final int rotation = displayFrames.mRotation;
+        final int displayHeight = displayFrames.mDisplayHeight;
+        final int displayWidth = displayFrames.mDisplayWidth;
+        final Rect dockFrame = displayFrames.mDock;
+        mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
+
+        if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+            // It's a system nav bar or a portrait screen; nav bar goes on bottom.
+            final int top = displayFrames.mUnrestricted.bottom
+                    - getNavigationBarHeight(rotation, uiMode);
+            mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
+            displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
+            if (transientNavBarShowing) {
+                mNavigationBarController.setBarShowingLw(true);
+            } else if (navVisible) {
+                mNavigationBarController.setBarShowingLw(true);
+                dockFrame.bottom = displayFrames.mRestricted.bottom
+                        = displayFrames.mRestrictedOverscan.bottom = top;
+            } else {
+                // We currently want to hide the navigation UI - unless we expanded the status bar.
+                mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
             }
-            // Make sure the content and current rectangles are updated to
-            // account for the restrictions from the navigation bar.
-            mContentTop = mVoiceContentTop = mCurTop = mDockTop;
-            mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
-            mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
-            mContentRight = mVoiceContentRight = mCurRight = mDockRight;
-            mStatusBarLayer = mNavigationBar.getSurfaceLayer();
-            // And compute the final frame.
-            mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
-                    mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
-                    mTmpNavigationFrame, mTmpNavigationFrame);
-            if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
-            if (mNavigationBarController.checkHiddenLw()) {
-                return true;
+            if (navVisible && !navTranslucent && !navAllowedHidden
+                    && !mNavigationBar.isAnimatingLw()
+                    && !mNavigationBarController.wasRecentlyTranslucent()) {
+                // If the opaque nav bar is currently requested to be visible and not in the process
+                // of animating on or off, then we can tell the app that it is covered by it.
+                displayFrames.mSystem.bottom = top;
+            }
+        } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+            // Landscape screen; nav bar goes to the right.
+            final int left = displayFrames.mUnrestricted.right
+                    - getNavigationBarWidth(rotation, uiMode);
+            mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
+            displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
+            if (transientNavBarShowing) {
+                mNavigationBarController.setBarShowingLw(true);
+            } else if (navVisible) {
+                mNavigationBarController.setBarShowingLw(true);
+                dockFrame.right = displayFrames.mRestricted.right
+                        = displayFrames.mRestrictedOverscan.right = left;
+            } else {
+                // We currently want to hide the navigation UI - unless we expanded the status bar.
+                mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+            }
+            if (navVisible && !navTranslucent && !navAllowedHidden
+                    && !mNavigationBar.isAnimatingLw()
+                    && !mNavigationBarController.wasRecentlyTranslucent()) {
+                // If the nav bar is currently requested to be visible, and not in the process of
+                // animating on or off, then we can tell the app that it is covered by it.
+                displayFrames.mSystem.right = left;
+            }
+        } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+            // Seascape screen; nav bar goes to the left.
+            final int right = displayFrames.mUnrestricted.left
+                    + getNavigationBarWidth(rotation, uiMode);
+            mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
+            displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
+            if (transientNavBarShowing) {
+                mNavigationBarController.setBarShowingLw(true);
+            } else if (navVisible) {
+                mNavigationBarController.setBarShowingLw(true);
+                dockFrame.left = displayFrames.mRestricted.left =
+                        displayFrames.mRestrictedOverscan.left = right;
+            } else {
+                // We currently want to hide the navigation UI - unless we expanded the status bar.
+                mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+            }
+            if (navVisible && !navTranslucent && !navAllowedHidden
+                    && !mNavigationBar.isAnimatingLw()
+                    && !mNavigationBarController.wasRecentlyTranslucent()) {
+                // If the nav bar is currently requested to be visible, and not in the process of
+                // animating on or off, then we can tell the app that it is covered by it.
+                displayFrames.mSystem.left = right;
             }
         }
-        return false;
+
+        // Make sure the content and current rectangles are updated to account for the restrictions
+        // from the navigation bar.
+        displayFrames.mCurrent.set(dockFrame);
+        displayFrames.mVoiceContent.set(dockFrame);
+        displayFrames.mContent.set(dockFrame);
+        mStatusBarLayer = mNavigationBar.getSurfaceLayer();
+        // And compute the final frame.
+        mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
+                mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
+                mTmpNavigationFrame, mTmpNavigationFrame);
+        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+        return mNavigationBarController.checkHiddenLw();
     }
 
     private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
@@ -4815,32 +4674,26 @@
         return 0;
     }
 
-    @Override
-    public void getContentRectLw(Rect r) {
-        r.set(mContentLeft, mContentTop, mContentRight, mContentBottom);
-    }
-
-    void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
-            boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf) {
+    private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
+            boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf,
+            DisplayFrames displayFrames) {
         if (win.getSurfaceLayer() > mDockLayer && attached.getSurfaceLayer() < mDockLayer) {
-            // Here's a special case: if this attached window is a panel that is
-            // above the dock window, and the window it is attached to is below
-            // the dock window, then the frames we computed for the window it is
-            // attached to can not be used because the dock is effectively part
-            // of the underlying window and the attached window is floating on top
-            // of the whole thing.  So, we ignore the attached window and explicitly
-            // compute the frames that would be appropriate without the dock.
-            df.left = of.left = cf.left = vf.left = mDockLeft;
-            df.top = of.top = cf.top = vf.top = mDockTop;
-            df.right = of.right = cf.right = vf.right = mDockRight;
-            df.bottom = of.bottom = cf.bottom = vf.bottom = mDockBottom;
+            // Here's a special case: if this attached window is a panel that is above the dock
+            // window, and the window it is attached to is below the dock window, then the frames we
+            // computed for the window it is attached to can not be used because the dock is
+            // effectively part of the underlying window and the attached window is floating on top
+            // of the whole thing. So, we ignore the attached window and explicitly compute the
+            // frames that would be appropriate without the dock.
+            vf.set(displayFrames.mDock);
+            cf.set(displayFrames.mDock);
+            of.set(displayFrames.mDock);
+            df.set(displayFrames.mDock);
         } else {
-            // The effective display frame of the attached window depends on
-            // whether it is taking care of insetting its content.  If not,
-            // we need to use the parent's content frame so that the entire
-            // window is positioned within that content.  Otherwise we can use
-            // the overscan frame and let the attached window take care of
-            // positioning its content appropriately.
+            // The effective display frame of the attached window depends on whether it is taking
+            // care of insetting its content. If not, we need to use the parent's content frame so
+            // that the entire window is positioned within that content. Otherwise we can use the
+            // overscan frame and let the attached window take care of positioning its content
+            // appropriately.
             if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
                 // Set the content frame of the attached window to the parent's decor frame
                 // (same as content frame when IME isn't present) if specifically requested by
@@ -4849,51 +4702,37 @@
                 cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
                         ? attached.getContentFrameLw() : attached.getOverscanFrameLw());
             } else {
-                // If the window is resizing, then we want to base the content
-                // frame on our attached content frame to resize...  however,
-                // things can be tricky if the attached window is NOT in resize
-                // mode, in which case its content frame will be larger.
-                // Ungh.  So to deal with that, make sure the content frame
-                // we end up using is not covering the IM dock.
+                // If the window is resizing, then we want to base the content frame on our attached
+                // content frame to resize...however, things can be tricky if the attached window is
+                // NOT in resize mode, in which case its content frame will be larger.
+                // Ungh. So to deal with that, make sure the content frame we end up using is not
+                // covering the IM dock.
                 cf.set(attached.getContentFrameLw());
                 if (attached.isVoiceInteraction()) {
-                    if (cf.left < mVoiceContentLeft) cf.left = mVoiceContentLeft;
-                    if (cf.top < mVoiceContentTop) cf.top = mVoiceContentTop;
-                    if (cf.right > mVoiceContentRight) cf.right = mVoiceContentRight;
-                    if (cf.bottom > mVoiceContentBottom) cf.bottom = mVoiceContentBottom;
+                    cf.intersectUnchecked(displayFrames.mVoiceContent);
                 } else if (attached.getSurfaceLayer() < mDockLayer) {
-                    if (cf.left < mContentLeft) cf.left = mContentLeft;
-                    if (cf.top < mContentTop) cf.top = mContentTop;
-                    if (cf.right > mContentRight) cf.right = mContentRight;
-                    if (cf.bottom > mContentBottom) cf.bottom = mContentBottom;
+                    cf.intersectUnchecked(displayFrames.mContent);
                 }
             }
             df.set(insetDecors ? attached.getDisplayFrameLw() : cf);
             of.set(insetDecors ? attached.getOverscanFrameLw() : cf);
             vf.set(attached.getVisibleFrameLw());
         }
-        // The LAYOUT_IN_SCREEN flag is used to determine whether the attached
-        // window should be positioned relative to its parent or the entire
-        // screen.
-        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0
-                ? attached.getFrameLw() : df);
+        // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
+        // positioned relative to its parent or the entire screen.
+        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
     }
 
-    private void applyStableConstraints(int sysui, int fl, Rect r) {
-        if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
-            // If app is requesting a stable layout, don't let the
-            // content insets go below the stable values.
-            if ((fl & FLAG_FULLSCREEN) != 0) {
-                if (r.left < mStableFullscreenLeft) r.left = mStableFullscreenLeft;
-                if (r.top < mStableFullscreenTop) r.top = mStableFullscreenTop;
-                if (r.right > mStableFullscreenRight) r.right = mStableFullscreenRight;
-                if (r.bottom > mStableFullscreenBottom) r.bottom = mStableFullscreenBottom;
-            } else {
-                if (r.left < mStableLeft) r.left = mStableLeft;
-                if (r.top < mStableTop) r.top = mStableTop;
-                if (r.right > mStableRight) r.right = mStableRight;
-                if (r.bottom > mStableBottom) r.bottom = mStableBottom;
-            }
+    private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
+        if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) {
+            return;
+        }
+        // If app is requesting a stable layout, don't let the content insets go below the stable
+        // values.
+        if ((fl & FLAG_FULLSCREEN) != 0) {
+            r.intersectUnchecked(displayFrames.mStableFullscreen);
+        } else {
+            r.intersectUnchecked(displayFrames.mStable);
         }
     }
 
@@ -4908,7 +4747,7 @@
 
     /** {@inheritDoc} */
     @Override
-    public void layoutWindowLw(WindowState win, WindowState attached) {
+    public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
         // We've already done the navigation bar, status bar, and all screen decor windows. If the
         // status bar can receive input, we need to layout it again to accommodate for the IME
         // window.
@@ -4922,9 +4761,10 @@
                 (win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
         if (needsToOffsetInputMethodTarget) {
             if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
-            offsetInputMethodWindowLw(mLastInputMethodWindow);
+            offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
         }
 
+        final int type = attrs.type;
         final int fl = PolicyControl.getWindowFlags(win, attrs);
         final int pfl = attrs.privateFlags;
         final int sim = attrs.softInputMode;
@@ -4945,120 +4785,83 @@
 
         final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
 
-        if (isDefaultDisplay) {
-            sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom);
-        } else {
-            sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom);
-        }
+        sf.set(displayFrames.mStable);
 
-        if (!isDefaultDisplay) {
-            // TODO: Need to fix this and above to take into account decor windows.
-            if (attached != null) {
-                // If this window is attached to another, our display
-                // frame is the same as the one we are attached to.
-                setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
-            } else {
-                // Give the window full screen.
-                pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
-                pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
-                pf.right = df.right = of.right = cf.right
-                        = mOverscanScreenLeft + mOverscanScreenWidth;
-                pf.bottom = df.bottom = of.bottom = cf.bottom
-                        = mOverscanScreenTop + mOverscanScreenHeight;
-            }
-        } else if (attrs.type == TYPE_INPUT_METHOD) {
-            pf.left = df.left = of.left = cf.left = vf.left = mDockLeft;
-            pf.top = df.top = of.top = cf.top = vf.top = mDockTop;
-            pf.right = df.right = of.right = cf.right = vf.right = mDockRight;
+        if (type == TYPE_INPUT_METHOD) {
+            vf.set(displayFrames.mDock);
+            cf.set(displayFrames.mDock);
+            of.set(displayFrames.mDock);
+            df.set(displayFrames.mDock);
+            pf.set(displayFrames.mDock);
             // IM dock windows layout below the nav bar...
-            pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+            pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
             // ...with content insets above the nav bar
-            cf.bottom = vf.bottom = mStableBottom;
+            cf.bottom = vf.bottom = displayFrames.mStable.bottom;
             if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
                 // The status bar forces the navigation bar while it's visible. Make sure the IME
                 // avoids the navigation bar in that case.
                 if (mNavigationBarPosition == NAV_BAR_RIGHT) {
-                    pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+                    pf.right = df.right = of.right = cf.right = vf.right =
+                            displayFrames.mStable.right;
                 } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
-                    pf.left = df.left = of.left = cf.left = vf.left = mStableLeft;
+                    pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
                 }
             }
             // IM dock windows always go to the bottom of the screen.
             attrs.gravity = Gravity.BOTTOM;
             mDockLayer = win.getSurfaceLayer();
-        } else if (attrs.type == TYPE_VOICE_INTERACTION) {
-            pf.left = df.left = of.left = mUnrestrictedScreenLeft;
-            pf.top = df.top = of.top = mUnrestrictedScreenTop;
-            pf.right = df.right = of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
-            pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+        } else if (type == TYPE_VOICE_INTERACTION) {
+            of.set(displayFrames.mUnrestricted);
+            df.set(displayFrames.mUnrestricted);
+            pf.set(displayFrames.mUnrestricted);
             if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                cf.left = mDockLeft;
-                cf.top = mDockTop;
-                cf.right = mDockRight;
-                cf.bottom = mDockBottom;
+                cf.set(displayFrames.mDock);
             } else {
-                cf.left = mContentLeft;
-                cf.top = mContentTop;
-                cf.right = mContentRight;
-                cf.bottom = mContentBottom;
+                cf.set(displayFrames.mContent);
             }
             if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                vf.left = mCurLeft;
-                vf.top = mCurTop;
-                vf.right = mCurRight;
-                vf.bottom = mCurBottom;
+                vf.set(displayFrames.mCurrent);
             } else {
                 vf.set(cf);
             }
-        } else if (attrs.type == TYPE_WALLPAPER) {
-           layoutWallpaper(win, pf, df, of, cf);
+        } else if (type == TYPE_WALLPAPER) {
+           layoutWallpaper(displayFrames, pf, df, of, cf);
         } else if (win == mStatusBar) {
-            pf.left = df.left = of.left = mUnrestrictedScreenLeft;
-            pf.top = df.top = of.top = mUnrestrictedScreenTop;
-            pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
-            pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight + mUnrestrictedScreenTop;
-            cf.left = vf.left = mStableLeft;
-            cf.top = vf.top = mStableTop;
-            cf.right = vf.right = mStableRight;
-            vf.bottom = mStableBottom;
+            of.set(displayFrames.mUnrestricted);
+            df.set(displayFrames.mUnrestricted);
+            pf.set(displayFrames.mUnrestricted);
+            cf.set(displayFrames.mStable);
+            vf.set(displayFrames.mStable);
 
             if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
-                cf.bottom = mContentBottom;
+                cf.bottom = displayFrames.mContent.bottom;
             } else {
-                cf.bottom = mDockBottom;
-                vf.bottom = mContentBottom;
+                cf.bottom = displayFrames.mDock.bottom;
+                vf.bottom = displayFrames.mContent.bottom;
             }
         } else {
-
-            // Default policy decor for the default display
-            dcf.left = mSystemLeft;
-            dcf.top = mSystemTop;
-            dcf.right = mSystemRight;
-            dcf.bottom = mSystemBottom;
-            final boolean inheritTranslucentDecor = (attrs.privateFlags
-                    & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
+            dcf.set(displayFrames.mSystem);
+            final boolean inheritTranslucentDecor =
+                    (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
             final boolean isAppWindow =
-                    attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW &&
-                    attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+                    type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
             final boolean topAtRest =
                     win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
             if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
                 if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
-                        && (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0
-                        && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0
-                        && (fl & WindowManager.LayoutParams.
-                                FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+                        && (fl & FLAG_FULLSCREEN) == 0
+                        && (fl & FLAG_TRANSLUCENT_STATUS) == 0
+                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                         && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
                     // Ensure policy decor includes status bar
-                    dcf.top = mStableTop;
+                    dcf.top = displayFrames.mStable.top;
                 }
-                if ((fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0
+                if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
                         && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
-                        && (fl & WindowManager.LayoutParams.
-                                FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
+                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
                     // Ensure policy decor includes navigation bar
-                    dcf.bottom = mStableBottom;
-                    dcf.right = mStableRight;
+                    dcf.bottom = displayFrames.mStable.bottom;
+                    dcf.right = displayFrames.mStable.right;
                 }
             }
 
@@ -5066,117 +4869,83 @@
                     == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
                 if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
                             + "): IN_SCREEN, INSET_DECOR");
-                // This is the case for a normal activity window: we want it
-                // to cover all of the screen space, and it can take care of
-                // moving its contents to account for screen decorations that
-                // intrude into that space.
+                // This is the case for a normal activity window: we want it to cover all of the
+                // screen space, and it can take care of moving its contents to account for screen
+                // decorations that intrude into that space.
                 if (attached != null) {
                     // If this window is attached to another, our display
                     // frame is the same as the one we are attached to.
-                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
+                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
+                            displayFrames);
                 } else {
-                    if (attrs.type == TYPE_STATUS_BAR_PANEL
-                            || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
-                        // Status bar panels are the only windows who can go on top of
-                        // the status bar.  They are protected by the STATUS_BAR_SERVICE
-                        // permission, so they have the same privileges as the status
-                        // bar itself.
+                    if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+                        // Status bar panels are the only windows who can go on top of the status
+                        // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
+                        // have the same privileges as the status bar itself.
                         //
                         // However, they should still dodge the navigation bar if it exists.
 
                         pf.left = df.left = of.left = hasNavBar
-                                ? mDockLeft : mUnrestrictedScreenLeft;
-                        pf.top = df.top = of.top = mUnrestrictedScreenTop;
+                                ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
+                        pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
                         pf.right = df.right = of.right = hasNavBar
-                                ? mRestrictedScreenLeft+mRestrictedScreenWidth
-                                : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+                                ? displayFrames.mRestricted.right
+                                : displayFrames.mUnrestricted.right;
                         pf.bottom = df.bottom = of.bottom = hasNavBar
-                                ? mRestrictedScreenTop+mRestrictedScreenHeight
-                                : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                                ? displayFrames.mRestricted.bottom
+                                : displayFrames.mUnrestricted.bottom;
 
                         if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
                                         "Laying out status bar window: (%d,%d - %d,%d)",
                                         pf.left, pf.top, pf.right, pf.bottom));
                     } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
-                            && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
-                            && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+                            && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
                         // Asking to layout into the overscan region, so give it that pure
                         // unrestricted area.
-                        pf.left = df.left = of.left = mOverscanScreenLeft;
-                        pf.top = df.top = of.top = mOverscanScreenTop;
-                        pf.right = df.right = of.right = mOverscanScreenLeft + mOverscanScreenWidth;
-                        pf.bottom = df.bottom = of.bottom = mOverscanScreenTop
-                                + mOverscanScreenHeight;
+                        of.set(displayFrames.mOverscan);
+                        df.set(displayFrames.mOverscan);
+                        pf.set(displayFrames.mOverscan);
                     } else if (canHideNavigationBar()
                             && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                            && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
-                            && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
-                        // Asking for layout as if the nav bar is hidden, lets the
-                        // application extend into the unrestricted overscan screen area.  We
-                        // only do this for application windows to ensure no window that
-                        // can be above the nav bar can do this.
-                        pf.left = df.left = mOverscanScreenLeft;
-                        pf.top = df.top = mOverscanScreenTop;
-                        pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
-                        pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
-                        // We need to tell the app about where the frame inside the overscan
-                        // is, so it can inset its content by that amount -- it didn't ask
-                        // to actually extend itself into the overscan region.
-                        of.left = mUnrestrictedScreenLeft;
-                        of.top = mUnrestrictedScreenTop;
-                        of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
-                        of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                            && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+                        // Asking for layout as if the nav bar is hidden, lets the application
+                        // extend into the unrestricted overscan screen area. We only do this for
+                        // application windows to ensure no window that can be above the nav bar can
+                        // do this.
+                        df.set(displayFrames.mOverscan);
+                        pf.set(displayFrames.mOverscan);
+                        // We need to tell the app about where the frame inside the overscan is, so
+                        // it can inset its content by that amount -- it didn't ask to actually
+                        // extend itself into the overscan region.
+                        of.set(displayFrames.mUnrestricted);
                     } else {
-                        pf.left = df.left = mRestrictedOverscanScreenLeft;
-                        pf.top = df.top = mRestrictedOverscanScreenTop;
-                        pf.right = df.right = mRestrictedOverscanScreenLeft
-                                + mRestrictedOverscanScreenWidth;
-                        pf.bottom = df.bottom = mRestrictedOverscanScreenTop
-                                + mRestrictedOverscanScreenHeight;
+                        df.set(displayFrames.mRestrictedOverscan);
+                        pf.set(displayFrames.mRestrictedOverscan);
                         // We need to tell the app about where the frame inside the overscan
                         // is, so it can inset its content by that amount -- it didn't ask
                         // to actually extend itself into the overscan region.
-                        of.left = mUnrestrictedScreenLeft;
-                        of.top = mUnrestrictedScreenTop;
-                        of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
-                        of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                        of.set(displayFrames.mUnrestricted);
                     }
 
                     if ((fl & FLAG_FULLSCREEN) == 0) {
                         if (win.isVoiceInteraction()) {
-                            cf.left = mVoiceContentLeft;
-                            cf.top = mVoiceContentTop;
-                            cf.right = mVoiceContentRight;
-                            cf.bottom = mVoiceContentBottom;
+                            cf.set(displayFrames.mVoiceContent);
                         } else {
                             if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                                cf.left = mDockLeft;
-                                cf.top = mDockTop;
-                                cf.right = mDockRight;
-                                cf.bottom = mDockBottom;
+                                cf.set(displayFrames.mDock);
                             } else {
-                                cf.left = mContentLeft;
-                                cf.top = mContentTop;
-                                cf.right = mContentRight;
-                                cf.bottom = mContentBottom;
+                                cf.set(displayFrames.mContent);
                             }
                         }
                     } else {
-                        // Full screen windows are always given a layout that is as if the
-                        // status bar and other transient decors are gone.  This is to avoid
-                        // bad states when moving from a window that is not hding the
-                        // status bar to one that is.
-                        cf.left = mRestrictedScreenLeft;
-                        cf.top = mRestrictedScreenTop;
-                        cf.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
-                        cf.bottom = mRestrictedScreenTop + mRestrictedScreenHeight;
+                        // Full screen windows are always given a layout that is as if the status
+                        // bar and other transient decors are gone. This is to avoid bad states when
+                        // moving from a window that is not hiding the status bar to one that is.
+                        cf.set(displayFrames.mRestricted);
                     }
-                    applyStableConstraints(sysUiFl, fl, cf);
+                    applyStableConstraints(sysUiFl, fl, cf, displayFrames);
                     if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                        vf.left = mCurLeft;
-                        vf.top = mCurTop;
-                        vf.right = mCurRight;
-                        vf.bottom = mCurBottom;
+                        vf.set(displayFrames.mCurrent);
                     } else {
                         vf.set(cf);
                     }
@@ -5184,86 +4953,70 @@
             } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl
                     & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                             | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
-                        "): IN_SCREEN");
+                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+                        + "): IN_SCREEN");
                 // A window that has requested to fill the entire screen just
                 // gets everything, period.
-                if (attrs.type == TYPE_STATUS_BAR_PANEL
-                        || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
-                    pf.left = df.left = of.left = cf.left = hasNavBar
-                            ? mDockLeft : mUnrestrictedScreenLeft;
-                    pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = of.right = cf.right = hasNavBar
-                            ? mRestrictedScreenLeft + mRestrictedScreenWidth
-                            : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom = hasNavBar
-                            ? mRestrictedScreenTop + mRestrictedScreenHeight
-                            : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+                    cf.set(displayFrames.mUnrestricted);
+                    of.set(displayFrames.mUnrestricted);
+                    df.set(displayFrames.mUnrestricted);
+                    pf.set(displayFrames.mUnrestricted);
+                    if (hasNavBar) {
+                        pf.left = df.left = of.left = cf.left = displayFrames.mDock.left;
+                        pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right;
+                        pf.bottom = df.bottom = of.bottom = cf.bottom =
+                                displayFrames.mRestricted.bottom;
+                    }
                     if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
                             "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
                             pf.left, pf.top, pf.right, pf.bottom));
-                } else if (attrs.type == TYPE_VOLUME_OVERLAY) {
+                } else if (type == TYPE_VOLUME_OVERLAY) {
                     // Volume overlay covers everything, including the status and navbar
-                    pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft;
-                    pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = of.right = cf.right =
-                            mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom =
-                            mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                    cf.set(displayFrames.mUnrestricted);
+                    of.set(displayFrames.mUnrestricted);
+                    df.set(displayFrames.mUnrestricted);
+                    pf.set(displayFrames.mUnrestricted);
                     if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
                                     "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
                                     pf.left, pf.top, pf.right, pf.bottom));
-                } else if (attrs.type == TYPE_NAVIGATION_BAR
-                        || attrs.type == TYPE_NAVIGATION_BAR_PANEL) {
+                } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
                     // The navigation bar has Real Ultimate Power.
-                    pf.left = df.left = of.left = mUnrestrictedScreenLeft;
-                    pf.top = df.top = of.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = of.right = mUnrestrictedScreenLeft
-                            + mUnrestrictedScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop
-                            + mUnrestrictedScreenHeight;
+                    of.set(displayFrames.mUnrestricted);
+                    df.set(displayFrames.mUnrestricted);
+                    pf.set(displayFrames.mUnrestricted);
                     if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
                                     "Laying out navigation bar window: (%d,%d - %d,%d)",
                                     pf.left, pf.top, pf.right, pf.bottom));
-                } else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
-                                || attrs.type == TYPE_BOOT_PROGRESS
-                                || attrs.type == TYPE_SCREENSHOT)
+                } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
                         && ((fl & FLAG_FULLSCREEN) != 0)) {
                     // Fullscreen secure system overlays get what they ask for. Screenshot region
                     // selection overlay should also expand to full screen.
-                    pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
-                    pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
-                    pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
-                            + mOverscanScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
-                            + mOverscanScreenHeight;
-                } else if (attrs.type == TYPE_BOOT_PROGRESS) {
+                    cf.set(displayFrames.mOverscan);
+                    of.set(displayFrames.mOverscan);
+                    df.set(displayFrames.mOverscan);
+                    pf.set(displayFrames.mOverscan);
+                } else if (type == TYPE_BOOT_PROGRESS) {
                     // Boot progress screen always covers entire display.
-                    pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
-                    pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
-                    pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
-                            + mOverscanScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
-                            + mOverscanScreenHeight;
+                    cf.set(displayFrames.mOverscan);
+                    of.set(displayFrames.mOverscan);
+                    df.set(displayFrames.mOverscan);
+                    pf.set(displayFrames.mOverscan);
                 } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
-                        && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
-                        && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
-                    // Asking to layout into the overscan region, so give it that pure
-                    // unrestricted area.
-                    pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
-                    pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
-                    pf.right = df.right = of.right = cf.right
-                            = mOverscanScreenLeft + mOverscanScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom
-                            = mOverscanScreenTop + mOverscanScreenHeight;
+                        && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+                    // Asking to layout into the overscan region, so give it that pure unrestricted
+                    // area.
+                    cf.set(displayFrames.mOverscan);
+                    of.set(displayFrames.mOverscan);
+                    df.set(displayFrames.mOverscan);
+                    pf.set(displayFrames.mOverscan);
                 } else if (canHideNavigationBar()
                         && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                        && (attrs.type == TYPE_STATUS_BAR
-                            || attrs.type == TYPE_TOAST
-                            || attrs.type == TYPE_DOCK_DIVIDER
-                            || attrs.type == TYPE_VOICE_INTERACTION_STARTING
-                            || (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
-                            && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) {
+                        && (type == TYPE_STATUS_BAR
+                            || type == TYPE_TOAST
+                            || type == TYPE_DOCK_DIVIDER
+                            || type == TYPE_VOICE_INTERACTION_STARTING
+                            || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
                     // Asking for layout as if the nav bar is hidden, lets the
                     // application extend into the unrestricted screen area.  We
                     // only do this for application windows (or toasts) to ensure no window that
@@ -5271,102 +5024,76 @@
                     // XXX This assumes that an app asking for this will also
                     // ask for layout in only content.  We can't currently figure out
                     // what the screen would be if only laying out to hide the nav bar.
-                    pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft;
-                    pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = of.right = cf.right = mUnrestrictedScreenLeft
-                            + mUnrestrictedScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom = mUnrestrictedScreenTop
-                            + mUnrestrictedScreenHeight;
+                    cf.set(displayFrames.mUnrestricted);
+                    of.set(displayFrames.mUnrestricted);
+                    df.set(displayFrames.mUnrestricted);
+                    pf.set(displayFrames.mUnrestricted);
                 } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
-                    pf.left = df.left = of.left = mRestrictedScreenLeft;
-                    pf.top = df.top = of.top  = mRestrictedScreenTop;
-                    pf.right = df.right = of.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = mRestrictedScreenTop
-                            + mRestrictedScreenHeight;
+                    of.set(displayFrames.mRestricted);
+                    df.set(displayFrames.mRestricted);
+                    pf.set(displayFrames.mRestricted);
                     if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                        cf.left = mDockLeft;
-                        cf.top = mDockTop;
-                        cf.right = mDockRight;
-                        cf.bottom = mDockBottom;
+                        cf.set(displayFrames.mDock);
                     } else {
-                        cf.left = mContentLeft;
-                        cf.top = mContentTop;
-                        cf.right = mContentRight;
-                        cf.bottom = mContentBottom;
+                        cf.set(displayFrames.mContent);
                     }
                 } else {
-                    pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
-                    pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
-                    pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft
-                            + mRestrictedScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop
-                            + mRestrictedScreenHeight;
+                    cf.set(displayFrames.mRestricted);
+                    of.set(displayFrames.mRestricted);
+                    df.set(displayFrames.mRestricted);
+                    pf.set(displayFrames.mRestricted);
                 }
 
-                applyStableConstraints(sysUiFl, fl, cf);
+                applyStableConstraints(sysUiFl, fl, cf,displayFrames);
 
                 if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                    vf.left = mCurLeft;
-                    vf.top = mCurTop;
-                    vf.right = mCurRight;
-                    vf.bottom = mCurBottom;
+                    vf.set(displayFrames.mCurrent);
                 } else {
                     vf.set(cf);
                 }
             } else if (attached != null) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
-                        "): attached to " + attached);
+                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+                        + "): attached to " + attached);
                 // A child window should be placed inside of the same visible
                 // frame that its parent had.
-                setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf);
+                setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf,
+                        displayFrames);
             } else {
                 if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
                         "): normal window");
                 // Otherwise, a normal window must be placed inside the content
                 // of all screen decorations.
-                if (attrs.type == TYPE_STATUS_BAR_PANEL || attrs.type == TYPE_VOLUME_OVERLAY) {
+                if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_VOLUME_OVERLAY) {
                     // Status bar panels and the volume dialog are the only windows who can go on
-                    // top of the status bar.  They are protected by the STATUS_BAR_SERVICE
-                    // permission, so they have the same privileges as the status
-                    // bar itself.
-                    pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
-                    pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
-                    pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft
-                            + mRestrictedScreenWidth;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop
-                            + mRestrictedScreenHeight;
-                } else if (attrs.type == TYPE_TOAST || attrs.type == TYPE_SYSTEM_ALERT) {
+                    // top of the status bar. They are protected by the STATUS_BAR_SERVICE
+                    // permission, so they have the same privileges as the status bar itself.
+                    cf.set(displayFrames.mRestricted);
+                    of.set(displayFrames.mRestricted);
+                    df.set(displayFrames.mRestricted);
+                    pf.set(displayFrames.mRestricted);
+                } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
                     // These dialogs are stable to interim decor changes.
-                    pf.left = df.left = of.left = cf.left = mStableLeft;
-                    pf.top = df.top = of.top = cf.top = mStableTop;
-                    pf.right = df.right = of.right = cf.right = mStableRight;
-                    pf.bottom = df.bottom = of.bottom = cf.bottom = mStableBottom;
+                    cf.set(displayFrames.mStable);
+                    of.set(displayFrames.mStable);
+                    df.set(displayFrames.mStable);
+                    pf.set(displayFrames.mStable);
                 } else {
-                    pf.left = mContentLeft;
-                    pf.top = mContentTop;
-                    pf.right = mContentRight;
-                    pf.bottom = mContentBottom;
+                    pf.set(displayFrames.mContent);
                     if (win.isVoiceInteraction()) {
-                        df.left = of.left = cf.left = mVoiceContentLeft;
-                        df.top = of.top = cf.top = mVoiceContentTop;
-                        df.right = of.right = cf.right = mVoiceContentRight;
-                        df.bottom = of.bottom = cf.bottom = mVoiceContentBottom;
+                        cf.set(displayFrames.mVoiceContent);
+                        of.set(displayFrames.mVoiceContent);
+                        df.set(displayFrames.mVoiceContent);
                     } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                        df.left = of.left = cf.left = mDockLeft;
-                        df.top = of.top = cf.top = mDockTop;
-                        df.right = of.right = cf.right = mDockRight;
-                        df.bottom = of.bottom = cf.bottom = mDockBottom;
+                        cf.set(displayFrames.mDock);
+                        of.set(displayFrames.mDock);
+                        df.set(displayFrames.mDock);
                     } else {
-                        df.left = of.left = cf.left = mContentLeft;
-                        df.top = of.top = cf.top = mContentTop;
-                        df.right = of.right = cf.right = mContentRight;
-                        df.bottom = of.bottom = cf.bottom = mContentBottom;
+                        cf.set(displayFrames.mContent);
+                        of.set(displayFrames.mContent);
+                        df.set(displayFrames.mContent);
                     }
                     if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                        vf.left = mCurLeft;
-                        vf.top = mCurTop;
-                        vf.right = mCurRight;
-                        vf.bottom = mCurBottom;
+                        vf.set(displayFrames.mCurrent);
                     } else {
                         vf.set(cf);
                     }
@@ -5376,11 +5103,11 @@
 
         // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
         // Also, we don't allow windows in multi-window mode to extend out of the screen.
-        if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR
+        if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
                 && !win.isInMultiWindowMode()) {
             df.left = df.top = -10000;
             df.right = df.bottom = 10000;
-            if (attrs.type != TYPE_WALLPAPER) {
+            if (type != TYPE_WALLPAPER) {
                 of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
                 of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
             }
@@ -5396,7 +5123,7 @@
             osf.set(cf.left, cf.top, cf.right, cf.bottom);
             int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
             if (outset > 0) {
-                int rotation = mDisplayRotation;
+                int rotation = displayFrames.mRotation;
                 if (rotation == Surface.ROTATION_0) {
                     osf.bottom += outset;
                 } else if (rotation == Surface.ROTATION_90) {
@@ -5413,7 +5140,7 @@
 
         if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
                 + ": sim=#" + Integer.toHexString(sim)
-                + " attach=" + attached + " type=" + attrs.type
+                + " attach=" + attached + " type=" + type
                 + String.format(" flags=0x%08x", fl)
                 + " pf=" + pf.toShortString() + " df=" + df.toShortString()
                 + " of=" + of.toShortString()
@@ -5426,62 +5153,42 @@
 
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
-        if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleLw()
+        if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
                 && !win.getGivenInsetsPendingLw()) {
             setLastInputMethodWindowLw(null, null);
-            offsetInputMethodWindowLw(win);
+            offsetInputMethodWindowLw(win, displayFrames);
         }
-        if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
+        if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
                 && !win.getGivenInsetsPendingLw()) {
-            offsetVoiceInputWindowLw(win);
+            offsetVoiceInputWindowLw(win, displayFrames);
         }
     }
 
-    private void layoutWallpaper(WindowState win, Rect pf, Rect df, Rect of, Rect cf) {
-
-        // The wallpaper also has Real Ultimate Power, but we want to tell
-        // it about the overscan area.
-        pf.left = df.left = mOverscanScreenLeft;
-        pf.top = df.top = mOverscanScreenTop;
-        pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
-        pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
-        of.left = cf.left = mUnrestrictedScreenLeft;
-        of.top = cf.top = mUnrestrictedScreenTop;
-        of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
-        of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+    private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) {
+        // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area.
+        df.set(displayFrames.mOverscan);
+        pf.set(displayFrames.mOverscan);
+        cf.set(displayFrames.mUnrestricted);
+        of.set(displayFrames.mUnrestricted);
     }
 
-    private void offsetInputMethodWindowLw(WindowState win) {
+    private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
         int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
         top += win.getGivenContentInsetsLw().top;
-        if (mContentBottom > top) {
-            mContentBottom = top;
-        }
-        if (mVoiceContentBottom > top) {
-            mVoiceContentBottom = top;
-        }
+        displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
+        displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
         top = win.getVisibleFrameLw().top;
         top += win.getGivenVisibleInsetsLw().top;
-        if (mCurBottom > top) {
-            mCurBottom = top;
-        }
+        displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
         if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
-                + mDockBottom + " mContentBottom="
-                + mContentBottom + " mCurBottom=" + mCurBottom);
+                + displayFrames.mDock.bottom + " mContentBottom="
+                + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom);
     }
 
-    private void offsetVoiceInputWindowLw(WindowState win) {
+    private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
         int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
         top += win.getGivenContentInsetsLw().top;
-        if (mVoiceContentBottom > top) {
-            mVoiceContentBottom = top;
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void finishLayoutLw() {
-        return;
+        displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
     }
 
     /** {@inheritDoc} */
@@ -8334,11 +8041,6 @@
     }
 
     @Override
-    public int getInputMethodWindowVisibleHeightLw() {
-        return mDockBottom - mCurBottom;
-    }
-
-    @Override
     public void setCurrentUserLw(int newUserId) {
         mCurrentUserId = newUserId;
         if (mKeyguardDelegate != null) {
@@ -8427,8 +8129,6 @@
     @Override
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        new Rect(mStableLeft, mStableTop, mStableRight, mStableBottom).writeToProto(proto,
-                STABLE_BOUNDS);
         proto.end(token);
     }
 
@@ -8534,58 +8234,6 @@
                 pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
         pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
                 pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
-        pw.print(prefix); pw.print("mOverscanScreen=("); pw.print(mOverscanScreenLeft);
-                pw.print(","); pw.print(mOverscanScreenTop);
-                pw.print(") "); pw.print(mOverscanScreenWidth);
-                pw.print("x"); pw.println(mOverscanScreenHeight);
-        if (mOverscanLeft != 0 || mOverscanTop != 0
-                || mOverscanRight != 0 || mOverscanBottom != 0) {
-            pw.print(prefix); pw.print("mOverscan left="); pw.print(mOverscanLeft);
-                    pw.print(" top="); pw.print(mOverscanTop);
-                    pw.print(" right="); pw.print(mOverscanRight);
-                    pw.print(" bottom="); pw.println(mOverscanBottom);
-        }
-        pw.print(prefix); pw.print("mRestrictedOverscanScreen=(");
-                pw.print(mRestrictedOverscanScreenLeft);
-                pw.print(","); pw.print(mRestrictedOverscanScreenTop);
-                pw.print(") "); pw.print(mRestrictedOverscanScreenWidth);
-                pw.print("x"); pw.println(mRestrictedOverscanScreenHeight);
-        pw.print(prefix); pw.print("mUnrestrictedScreen=("); pw.print(mUnrestrictedScreenLeft);
-                pw.print(","); pw.print(mUnrestrictedScreenTop);
-                pw.print(") "); pw.print(mUnrestrictedScreenWidth);
-                pw.print("x"); pw.println(mUnrestrictedScreenHeight);
-        pw.print(prefix); pw.print("mRestrictedScreen=("); pw.print(mRestrictedScreenLeft);
-                pw.print(","); pw.print(mRestrictedScreenTop);
-                pw.print(") "); pw.print(mRestrictedScreenWidth);
-                pw.print("x"); pw.println(mRestrictedScreenHeight);
-        pw.print(prefix); pw.print("mStableFullscreen=("); pw.print(mStableFullscreenLeft);
-                pw.print(","); pw.print(mStableFullscreenTop);
-                pw.print(")-("); pw.print(mStableFullscreenRight);
-                pw.print(","); pw.print(mStableFullscreenBottom); pw.println(")");
-        pw.print(prefix); pw.print("mStable=("); pw.print(mStableLeft);
-                pw.print(","); pw.print(mStableTop);
-                pw.print(")-("); pw.print(mStableRight);
-                pw.print(","); pw.print(mStableBottom); pw.println(")");
-        pw.print(prefix); pw.print("mSystem=("); pw.print(mSystemLeft);
-                pw.print(","); pw.print(mSystemTop);
-                pw.print(")-("); pw.print(mSystemRight);
-                pw.print(","); pw.print(mSystemBottom); pw.println(")");
-        pw.print(prefix); pw.print("mCur=("); pw.print(mCurLeft);
-                pw.print(","); pw.print(mCurTop);
-                pw.print(")-("); pw.print(mCurRight);
-                pw.print(","); pw.print(mCurBottom); pw.println(")");
-        pw.print(prefix); pw.print("mContent=("); pw.print(mContentLeft);
-                pw.print(","); pw.print(mContentTop);
-                pw.print(")-("); pw.print(mContentRight);
-                pw.print(","); pw.print(mContentBottom); pw.println(")");
-        pw.print(prefix); pw.print("mVoiceContent=("); pw.print(mVoiceContentLeft);
-                pw.print(","); pw.print(mVoiceContentTop);
-                pw.print(")-("); pw.print(mVoiceContentRight);
-                pw.print(","); pw.print(mVoiceContentBottom); pw.println(")");
-        pw.print(prefix); pw.print("mDock=("); pw.print(mDockLeft);
-                pw.print(","); pw.print(mDockTop);
-                pw.print(")-("); pw.print(mDockRight);
-                pw.print(","); pw.print(mDockBottom); pw.println(")");
         pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
                 pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
         pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 273b945..0c73fe8 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -56,6 +56,9 @@
     private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
     private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
     private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
+    private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs";
+    private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
+    private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
 
     private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -141,6 +144,21 @@
      */
     private float mAdjustBrightnessFactor;
 
+    /**
+     * Whether to put all apps in the stand-by mode or not for job scheduler.
+     */
+    private boolean mForceAllAppsStandbyJobs;
+
+    /**
+     * Whether to put all apps in the stand-by mode or not for alarms.
+     */
+    private boolean mForceAllAppsStandbyAlarms;
+
+    /**
+     * Weather to show non-essential sensors (e.g. edge sensors) or not.
+     */
+    private boolean mOptionalSensorsDisabled;
+
     private ContentResolver mContentResolver;
 
     public BatterySaverPolicy(Handler handler) {
@@ -180,10 +198,14 @@
             mAdjustBrightnessDisabled = mParser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
             mAdjustBrightnessFactor = mParser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
             mDataSaverDisabled = mParser.getBoolean(KEY_DATASAVER_DISABLED, true);
+            mForceAllAppsStandbyJobs = mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
+            mForceAllAppsStandbyAlarms =
+                    mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
+            mOptionalSensorsDisabled = mParser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
 
             // Get default value from Settings.Secure
             final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
-                    GPS_MODE_DISABLED_WHEN_SCREEN_OFF);
+                    GPS_MODE_NO_CHANGE);
             mGpsMode = mParser.getInt(KEY_GPS_MODE, defaultGpsMode);
         }
     }
@@ -235,6 +257,15 @@
                 case ServiceType.VIBRATION:
                     return builder.setBatterySaverEnabled(mVibrationDisabled)
                             .build();
+                case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS:
+                    return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs)
+                            .build();
+                case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS:
+                    return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms)
+                            .build();
+                case ServiceType.OPTIONAL_SENSORS:
+                    return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
+                            .build();
                 default:
                     return builder.setBatterySaverEnabled(realMode)
                             .build();
@@ -259,6 +290,8 @@
         pw.println("  " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
         pw.println("  " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
         pw.println("  " + KEY_GPS_MODE + "=" + mGpsMode);
-
+        pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
+        pw.println("  " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
+        pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
     }
 }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 416a606..a153fdf 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -71,6 +71,8 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.WindowManagerPolicy;
+import android.widget.Toast;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
@@ -1022,6 +1024,13 @@
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                             Manifest.permission.DEVICE_POWER);
+
+                    // STOPSHIP Remove the toast.
+                    if (mLowPowerModeEnabled) {
+                        Toast.makeText(mContext,
+                                com.android.internal.R.string.battery_saver_warning,
+                                Toast.LENGTH_LONG).show();
+                    }
                 }
             });
         }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2488723..f41ff2c 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -44,6 +44,8 @@
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
+import com.android.internal.os.KernelCpuSpeedReader;
+import com.android.internal.os.PowerProfile;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
@@ -71,6 +73,9 @@
     private final PendingIntent mPullingAlarmIntent;
     private final BroadcastReceiver mAppUpdateReceiver;
     private final BroadcastReceiver mUserUpdateReceiver;
+    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+    private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
 
     public StatsCompanionService(Context context) {
         super();
@@ -103,6 +108,16 @@
             }
         };
         Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
+        PowerProfile powerProfile = new PowerProfile(context);
+        final int numClusters = powerProfile.getNumCpuClusters();
+        mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
+        int firstCpuOfCluster = 0;
+        for (int i = 0; i < numClusters; i++) {
+            final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
+            mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
+                            numSpeedSteps);
+            firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
+        }
     }
 
     private final static int[] toIntArray(List<Integer> list) {
@@ -286,9 +301,6 @@
       }
     }
 
-    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
-    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
-
     private StatsLogEventWrapper[] addNetworkStats(int tag, NetworkStats stats, boolean withFGBG) {
         List<StatsLogEventWrapper> ret = new ArrayList<>();
         int size = stats.size();
@@ -446,6 +458,22 @@
                 }
                 return ret.toArray(new StatsLogEventWrapper[ret.size()]);
             }
+            case StatsLog.CPU_TIME_PER_FREQ_PULLED: {
+                List<StatsLogEventWrapper> ret = new ArrayList();
+                for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+                    long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readDelta();
+                    if (clusterTimeMs != null) {
+                        for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
+                            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+                            e.writeInt(tagId);
+                            e.writeInt(speed);
+                            e.writeLong(clusterTimeMs[speed]);
+                            ret.add(e);
+                        }
+                    }
+                }
+                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 67d62e1..c4b810f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -104,6 +104,7 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
 import static com.android.server.wm.proto.DisplayProto.ABOVE_APP_WINDOWS;
 import static com.android.server.wm.proto.DisplayProto.BELOW_APP_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.DISPLAY_FRAMES;
 import static com.android.server.wm.proto.DisplayProto.DISPLAY_INFO;
 import static com.android.server.wm.proto.DisplayProto.DOCKED_STACK_DIVIDER_CONTROLLER;
 import static com.android.server.wm.proto.DisplayProto.DPI;
@@ -147,6 +148,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.view.IInputMethodClient;
+import android.view.DisplayFrames;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -222,6 +224,8 @@
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private final Display mDisplay;
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+    DisplayFrames mDisplayFrames;
+
     /**
      * For default display it contains real metrics, empty for others.
      * @see WindowManagerService#createWatermarkInTransaction()
@@ -285,7 +289,6 @@
     private boolean mLastWallpaperVisible = false;
 
     private Rect mBaseDisplayRect = new Rect();
-    private Rect mContentRect = new Rect();
 
     // Accessed directly by all users.
     private boolean mLayoutNeeded;
@@ -545,7 +548,7 @@
                 w.mLayoutNeeded = false;
                 w.prelayout();
                 final boolean firstLayout = !w.isLaidOut();
-                mService.mPolicy.layoutWindowLw(w, null);
+                mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames);
                 w.mLayoutSeq = mService.mLayoutSeq;
 
                 // If this is the first layout, we need to initialize the last inset values as
@@ -586,7 +589,7 @@
                 }
                 w.mLayoutNeeded = false;
                 w.prelayout();
-                mService.mPolicy.layoutWindowLw(w, w.getParentWindow());
+                mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
                 w.mLayoutSeq = mService.mLayoutSeq;
                 if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
                         + " mContainingFrame=" + w.mContainingFrame
@@ -758,6 +761,7 @@
         display.getMetrics(mDisplayMetrics);
         isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
         mService = service;
+        mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo);
         initializeDisplayBaseInfo();
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
@@ -1129,6 +1133,13 @@
         return true;
     }
 
+    void configureDisplayPolicy() {
+        mService.mPolicy.setInitialDisplaySize(getDisplay(),
+                mBaseDisplayWidth, mBaseDisplayHeight, mBaseDisplayDensity);
+
+        mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo);
+    }
+
     /**
      * Update {@link #mDisplayInfo} and other internal variables when display is rotated or config
      * changed.
@@ -1744,7 +1755,7 @@
     }
 
     void getContentRect(Rect out) {
-        out.set(mContentRect);
+        out.set(mDisplayFrames.mContent);
     }
 
     TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
@@ -1847,8 +1858,8 @@
             mTmpRect2.setEmpty();
             for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                 final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
-                stack.setTouchExcludeRegion(
-                        focusedTask, delta, mTouchExcludeRegion, mContentRect, mTmpRect2);
+                stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
+                        mDisplayFrames.mContent, mTmpRect2);
             }
             // If we removed the focused task above, add it back and only leave its
             // outside touch area in the exclusion. TapDectector is not interested in
@@ -2025,7 +2036,7 @@
         final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
         final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
         final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock();
-        final int imeHeight = mService.mPolicy.getInputMethodWindowVisibleHeightLw();
+        final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
         final boolean imeHeightChanged = imeVisible &&
                 imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
 
@@ -2167,6 +2178,7 @@
         if (screenRotationAnimation != null) {
             screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
         }
+        mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
         proto.end(token);
     }
 
@@ -2246,6 +2258,9 @@
             pw.println(subPrefix
                     + "mInputMethodAnimLayerAdjustment=" + mInputMethodAnimLayerAdjustment);
         }
+
+        pw.println();
+        mDisplayFrames.dump(prefix, pw);
     }
 
     @Override
@@ -2871,21 +2886,22 @@
 
         final int dw = mDisplayInfo.logicalWidth;
         final int dh = mDisplayInfo.logicalHeight;
-
         if (DEBUG_LAYOUT) {
             Slog.v(TAG, "-------------------------------------");
             Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
         }
 
-        mService.mPolicy.beginLayoutLw(mDisplayId, dw, dh, mRotation, getConfiguration().uiMode);
+        mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo);
+        // TODO: Not sure if we really need to set the rotation here since we are updating from the
+        // display info above...
+        mDisplayFrames.mRotation = mRotation;
+        mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
         if (isDefaultDisplay) {
             // Not needed on non-default displays.
             mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
             mService.mScreenRect.set(0, 0, dw, dh);
         }
 
-        mService.mPolicy.getContentRectLw(mContentRect);
-
         int seq = mService.mLayoutSeq + 1;
         if (seq < 0) seq = 0;
         mService.mLayoutSeq = seq;
@@ -2915,7 +2931,6 @@
             mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
         }
 
-        mService.mPolicy.finishLayoutLw();
         mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
     }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 5ea0e1d..e693e5a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -23,6 +23,7 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
@@ -116,6 +117,7 @@
     private final DimLayer mDimLayer;
 
     private boolean mMinimizedDock;
+    private int mOriginalDockedSide = DOCKED_INVALID;
     private boolean mAnimatingForMinimizedDockedStack;
     private boolean mAnimationStarted;
     private long mAnimationStartTime;
@@ -408,6 +410,31 @@
         mDockedStackListeners.finishBroadcast();
     }
 
+    /**
+     * Checks if the primary stack is allowed to dock to a specific side based on its original dock
+     * side.
+     *
+     * @param dockSide the side to see if it is valid
+     * @return true if the side provided is valid
+     */
+    boolean canPrimaryStackDockTo(int dockSide) {
+        if (mService.mPolicy.isDockSideAllowed(dockSide)) {
+            // Side is the same as original side
+            if (dockSide == mOriginalDockedSide) {
+                return true;
+            }
+            // Special rule that the top in portrait is always valid
+            if (dockSide == DOCKED_TOP) {
+                return true;
+            }
+            // Only if original docked side was top in portrait will allow left side for landscape
+            if (dockSide == DOCKED_LEFT && mOriginalDockedSide == DOCKED_TOP) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void notifyDockedStackExistsChanged(boolean exists) {
         // TODO(multi-display): Perform all actions only for current display.
         final int size = mDockedStackListeners.beginBroadcast();
@@ -430,8 +457,11 @@
                 inputMethodManagerInternal.hideCurrentInputMethod();
                 mImeHideRequested = true;
             }
+            final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+            mOriginalDockedSide = stack.getDockSide();
             return;
         }
+        mOriginalDockedSide = DOCKED_INVALID;
         setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
     }
 
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 76e25ba..4567e10 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.NonNull;
 import android.content.ClipData;
 import android.graphics.PixelFormat;
 import android.os.Binder;
@@ -35,6 +36,8 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
+import android.view.WindowManagerInternal.IDragDropCallback;
+import com.android.internal.util.Preconditions;
 import com.android.server.input.InputWindowHandle;
 
 /**
@@ -61,6 +64,7 @@
 
     private WindowManagerService mService;
     private final Handler mHandler;
+
     /**
      * Lock to preserve the order of state updates.
      * The lock is used to process drag and drop state updates in order without having the window
@@ -94,6 +98,11 @@
      */
     private final Object mWriteLock = new Object();
 
+    /**
+     * Callback which is used to sync drag state with the vendor-specific code.
+     */
+    @NonNull private IDragDropCallback mCallback = new IDragDropCallback() {};
+
     boolean dragDropActiveLocked() {
         return mDragState != null;
     }
@@ -102,6 +111,13 @@
         return mDragState.getInputWindowHandle();
     }
 
+    void registerCallback(IDragDropCallback callback) {
+        Preconditions.checkNotNull(callback);
+        synchronized (mWriteLock) {
+            mCallback = callback;
+        }
+    }
+
     DragDropController(WindowManagerService service, Looper looper) {
         mService = service;
         mHandler = new DragHandler(service, looper);
@@ -169,6 +185,10 @@
         }
 
         synchronized (mWriteLock) {
+            if (!mCallback.performDrag(window, dragToken, touchSource, touchX, touchY, thumbCenterX,
+                    thumbCenterY, data)) {
+                return false;
+            }
             synchronized (mService.mWindowMap) {
                 if (mDragState == null) {
                     Slog.w(TAG_WM, "No drag prepared");
@@ -251,6 +271,7 @@
         }
 
         synchronized (mWriteLock) {
+            mCallback.reportDropResult(window, consumed);
             synchronized (mService.mWindowMap) {
                 if (mDragState == null) {
                     // Most likely the drop recipient ANRed and we ended the drag
@@ -288,6 +309,7 @@
         }
 
         synchronized (mWriteLock) {
+            mCallback.cancelDragAndDrop(dragToken);
             synchronized (mService.mWindowMap) {
                 if (mDragState == null) {
                     Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bcb6e673..f541926 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -247,7 +247,7 @@
         if (mService.mDisplayManagerInternal != null) {
             mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
                     displayId, displayInfo);
-            mService.configureDisplayPolicyLocked(dc);
+            dc.configureDisplayPolicy();
 
             // Tap Listeners are supported for:
             // 1. All physical displays (multi-display).
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6e89e3e..dde7946 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -442,7 +442,7 @@
         mTmpRect2.set(mBounds);
         mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
         if (inSplitScreenPrimaryWindowingMode()) {
-            repositionDockedStackAfterRotation(mTmpRect2);
+            repositionPrimarySplitScreenStackAfterRotation(mTmpRect2);
             snapDockedStackAfterRotation(mTmpRect2);
             final int newDockSide = getDockSide(mTmpRect2);
 
@@ -466,14 +466,14 @@
     }
 
     /**
-     * Some dock sides are not allowed by the policy. This method queries the policy and moves
-     * the docked stack around if needed.
+     * Some primary split screen sides are not allowed by the policy. This method queries the policy
+     * and moves the primary stack around if needed.
      *
-     * @param inOutBounds the bounds of the docked stack to adjust
+     * @param inOutBounds the bounds of the primary stack to adjust
      */
-    private void repositionDockedStackAfterRotation(Rect inOutBounds) {
+    private void repositionPrimarySplitScreenStackAfterRotation(Rect inOutBounds) {
         int dockSide = getDockSide(inOutBounds);
-        if (mService.mPolicy.isDockSideAllowed(dockSide)) {
+        if (mDisplayContent.getDockedDividerController().canPrimaryStackDockTo(dockSide)) {
             return;
         }
         mDisplayContent.getLogicalDisplayRect(mTmpRect);
@@ -603,6 +603,14 @@
         } else {
             maxPosition = computeMaxPosition(maxPosition);
         }
+
+        // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
+        if (targetPosition == POSITION_BOTTOM && minPosition == 0) {
+            return POSITION_BOTTOM;
+        } else if (targetPosition == POSITION_TOP
+                && maxPosition == (addingNew ? stackSize : stackSize - 1)) {
+            return POSITION_TOP;
+        }
         // Reset position based on minimum/maximum possible positions.
         return Math.min(Math.max(targetPosition, minPosition), maxPosition);
     }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 65f8cdf..b3bb0b7 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -103,6 +103,10 @@
         }
     }
 
+    public void positionChildAtTop(AppWindowContainerController childController) {
+        positionChildAt(childController, POSITION_TOP);
+    }
+
     public void positionChildAt(AppWindowContainerController childController, int position) {
         synchronized(mService.mWindowMap) {
             final AppWindowToken aToken = childController.mContainer;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f313f31..df51be1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -125,6 +125,7 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
+import android.content.ClipData;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -244,6 +245,7 @@
 import com.android.server.UiThread;
 import com.android.server.Watchdog;
 import com.android.server.input.InputManagerService;
+import android.view.DisplayFrames;
 import com.android.server.power.ShutdownThread;
 import com.android.server.utils.PriorityDump;
 
@@ -568,6 +570,8 @@
     boolean mForceResizableTasks = false;
     boolean mSupportsPictureInPicture = false;
 
+    private boolean mDisableTransitionAnimation = false;
+
     int getDragLayerLocked() {
         return mPolicy.getWindowLayerFromTypeLw(TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
     }
@@ -965,6 +969,8 @@
                 com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
         mMaxUiWidth = context.getResources().getInteger(
                 com.android.internal.R.integer.config_maxUiWidth);
+        mDisableTransitionAnimation = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableTransitionAnimation);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplaySettings = new DisplaySettings();
@@ -1447,23 +1453,19 @@
                 prepareNoneTransitionForRelaunching(atoken);
             }
 
-            if (displayContent.isDefaultDisplay) {
-                final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-                final Rect taskBounds;
-                if (atoken != null && atoken.getTask() != null) {
-                    taskBounds = mTmpRect;
-                    atoken.getTask().getBounds(mTmpRect);
-                } else {
-                    taskBounds = null;
-                }
-                if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayInfo.rotation,
-                        displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
-                        outStableInsets, outOutsets)) {
-                    res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
-                }
+            final DisplayFrames displayFrames = displayContent.mDisplayFrames;
+            // TODO: Not sure if onDisplayInfoUpdated() call is needed.
+            displayFrames.onDisplayInfoUpdated(displayContent.getDisplayInfo());
+            final Rect taskBounds;
+            if (atoken != null && atoken.getTask() != null) {
+                taskBounds = mTmpRect;
+                atoken.getTask().getBounds(mTmpRect);
             } else {
-                outContentInsets.setEmpty();
-                outStableInsets.setEmpty();
+                taskBounds = null;
+            }
+            if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayFrames, outContentInsets,
+                    outStableInsets, outOutsets)) {
+                res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
 
             if (mInTouchMode) {
@@ -2278,6 +2280,14 @@
 
     boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp,
             int transit, boolean enter, boolean isVoiceInteraction) {
+        if (mDisableTransitionAnimation) {
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
+                Slog.v(TAG_WM,
+                        "applyAnimation: transition animation is disabled. atoken=" + atoken);
+            }
+            atoken.mAppAnimator.clearAnimation();
+            return false;
+        }
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
@@ -2916,10 +2926,9 @@
     }
 
     public void setKeyguardGoingAway(boolean keyguardGoingAway) {
-// TODO: Use of this can be removed. Revert ag/I8369723d6a77f2c602f1ef080371fa7cd9ee094e
-//        synchronized (mWindowMap) {
-//            mKeyguardGoingAway = keyguardGoingAway;
-//        }
+        synchronized (mWindowMap) {
+            mKeyguardGoingAway = keyguardGoingAway;
+        }
     }
 
     // -------------------------------------------------------------
@@ -4656,7 +4665,7 @@
         synchronized(mWindowMap) {
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
-            configureDisplayPolicyLocked(getDefaultDisplayContentLocked());
+            getDefaultDisplayContentLocked().configureDisplayPolicy();
         }
 
         try {
@@ -5513,7 +5522,7 @@
         if (!mDisplayReady) {
             return;
         }
-        configureDisplayPolicyLocked(displayContent);
+        displayContent.configureDisplayPolicy();
         displayContent.setLayoutNeeded();
 
         final int displayId = displayContent.getDisplayId();
@@ -5534,18 +5543,6 @@
         mWindowPlacerLocked.performSurfacePlacement();
     }
 
-    void configureDisplayPolicyLocked(DisplayContent displayContent) {
-        mPolicy.setInitialDisplaySize(displayContent.getDisplay(),
-                displayContent.mBaseDisplayWidth,
-                displayContent.mBaseDisplayHeight,
-                displayContent.mBaseDisplayDensity);
-
-        DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        mPolicy.setDisplayOverscan(displayContent.getDisplay(),
-                displayInfo.overscanLeft, displayInfo.overscanTop,
-                displayInfo.overscanRight, displayInfo.overscanBottom);
-    }
-
     /**
      * Get an array with display ids ordered by focus priority - last items should be given
      * focus first. Sparse array just maps position to displayId.
@@ -7278,11 +7275,6 @@
         }
 
         @Override
-        public boolean isKeyguardGoingAway() {
-            return WindowManagerService.this.mKeyguardGoingAway;
-        }
-
-        @Override
         public boolean isKeyguardShowingAndNotOccluded() {
             return WindowManagerService.this.isKeyguardShowingAndNotOccluded();
         }
@@ -7363,7 +7355,9 @@
         @Override
         public int getInputMethodWindowVisibleHeight() {
             synchronized (mWindowMap) {
-                return mPolicy.getInputMethodWindowVisibleHeightLw();
+                // TODO(multi-display): Have caller pass in the display they are interested in.
+                final DisplayContent dc = getDefaultDisplayContentLocked();
+                return dc.mDisplayFrames.getInputMethodWindowVisibleHeight();
             }
         }
 
@@ -7447,6 +7441,11 @@
                 mVr2dDisplayId = vr2dDisplayId;
             }
         }
+
+        @Override
+        public void registerDragDropControllerCallback(IDragDropCallback callback) {
+            mDragDropController.registerCallback(callback);
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
index 35e65bc..df85e31 100644
--- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -17,18 +17,16 @@
 #define LOG_TAG "UsbHostManagerJNI"
 #include "utils/Log.h"
 
-#include <stdlib.h>
-
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 
 #include <usbhost/usbhost.h>
 
-#define MAX_DESCRIPTORS_LENGTH 4096
+#define MAX_DESCRIPTORS_LENGTH 16384
 
 // com.android.server.usb.descriptors
 extern "C" {
-jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors_1native(
+jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors(
         JNIEnv* env, jobject thiz, jstring deviceAddr) {
     const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
     struct usb_device* device = usb_device_open(deviceAddrStr);
@@ -41,7 +39,6 @@
 
     int fd = usb_device_get_fd(device);
     if (fd < 0) {
-        usb_device_close(device);
         return NULL;
     }
 
@@ -49,48 +46,17 @@
     jbyte buffer[MAX_DESCRIPTORS_LENGTH];
     lseek(fd, 0, SEEK_SET);
     int numBytes = read(fd, buffer, sizeof(buffer));
-    jbyteArray ret = NULL;
+
     usb_device_close(device);
 
-    if (numBytes > 0) {
+    jbyteArray ret = NULL;
+    if (numBytes != 0) {
         ret = env->NewByteArray(numBytes);
         env->SetByteArrayRegion(ret, 0, numBytes, buffer);
-    } else {
-        ALOGE("error reading descriptors\n");
     }
-
     return ret;
 }
 
-jstring JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getDescriptorString_1native(
-        JNIEnv* env, jobject thiz, jstring deviceAddr, jint stringId) {
-
-    const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
-    struct usb_device* device = usb_device_open(deviceAddrStr);
-    env->ReleaseStringUTFChars(deviceAddr, deviceAddrStr);
-
-    if (!device) {
-        ALOGE("usb_device_open failed");
-        return NULL;
-    }
-
-    int fd = usb_device_get_fd(device);
-    if (fd < 0) {
-        ALOGE("usb_device_get_fd failed");
-        usb_device_close(device);
-        return NULL;
-    }
-
-    char* c_str = usb_device_get_string(device, stringId, 0 /*timeout*/);
-
-    jstring j_str = env->NewStringUTF(c_str);
-
-    free(c_str);
-    usb_device_close(device);
-
-    return j_str;
-}
-
 } // extern "C"
 
 
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index 24f2014..88ae824 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -22,6 +22,8 @@
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
 
+#include <usbhost/usbhost.h>
+
 #include <stdio.h>
 #include <asm/byteorder.h>
 #include <sys/types.h>
@@ -29,20 +31,22 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 
-#include <usbhost/usbhost.h>
-
-#define MAX_DESCRIPTORS_LENGTH 4096
-
 namespace android
 {
 
+static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
 static struct parcel_file_descriptor_offsets_t
 {
     jclass mClass;
     jmethodID mConstructor;
 } gParcelFileDescriptorOffsets;
 
-static jmethodID method_usbDeviceAdded;
+static jmethodID method_beginUsbDeviceAdded;
+static jmethodID method_addUsbConfiguration;
+static jmethodID method_addUsbInterface;
+static jmethodID method_addUsbEndpoint;
+static jmethodID method_endUsbDeviceAdded;
 static jmethodID method_usbDeviceRemoved;
 
 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -53,52 +57,101 @@
     }
 }
 
-static int usb_device_added(const char *devAddress, void* clientData) {
-    struct usb_device *device = usb_device_open(devAddress);
+static int usb_device_added(const char *devname, void* client_data) {
+    struct usb_descriptor_header* desc;
+    struct usb_descriptor_iter iter;
+
+    struct usb_device *device = usb_device_open(devname);
     if (!device) {
         ALOGE("usb_device_open failed\n");
         return 0;
     }
 
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jobject thiz = (jobject)client_data;
     const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
-    int classID = deviceDesc->bDeviceClass;
-    int subClassID = deviceDesc->bDeviceSubClass;
 
-    // get the raw descriptors
-    int numBytes = usb_device_get_descriptors_length(device);
-    if (numBytes > 0) {
-        JNIEnv* env = AndroidRuntime::getJNIEnv();
-        jobject thiz = (jobject)clientData;
-        jstring deviceAddress = env->NewStringUTF(devAddress);
+    char *manufacturer = usb_device_get_manufacturer_name(device,
+            USB_CONTROL_TRANSFER_TIMEOUT_MS);
+    char *product = usb_device_get_product_name(device,
+            USB_CONTROL_TRANSFER_TIMEOUT_MS);
+    int version = usb_device_get_version(device);
+    char *serial = usb_device_get_serial(device,
+            USB_CONTROL_TRANSFER_TIMEOUT_MS);
 
-        jbyteArray descriptorsArray = env->NewByteArray(numBytes);
-        const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
-        env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
+    jstring deviceName = env->NewStringUTF(devname);
+    jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer);
+    jstring productName = AndroidRuntime::NewStringLatin1(env, product);
+    jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial);
 
-        env->CallBooleanMethod(thiz, method_usbDeviceAdded,
-                deviceAddress, classID, subClassID, descriptorsArray);
+    jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded,
+            deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device),
+            deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol,
+            manufacturerName, productName, version, serialNumber);
 
-        env->DeleteLocalRef(descriptorsArray);
-        env->DeleteLocalRef(deviceAddress);
+    env->DeleteLocalRef(serialNumber);
+    env->DeleteLocalRef(productName);
+    env->DeleteLocalRef(manufacturerName);
+    env->DeleteLocalRef(deviceName);
+    free(manufacturer);
+    free(product);
+    free(serial);
 
-        checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    } else {
-        // TODO return an error code here?
-        ALOGE("error reading descriptors\n");
+    if (!result) goto fail;
+
+    usb_descriptor_iter_init(device, &iter);
+
+    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+        if (desc->bDescriptorType == USB_DT_CONFIG) {
+            struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc;
+            char *name = usb_device_get_string(device, config->iConfiguration,
+                    USB_CONTROL_TRANSFER_TIMEOUT_MS);
+            jstring configName = AndroidRuntime::NewStringLatin1(env, name);
+
+            env->CallVoidMethod(thiz, method_addUsbConfiguration,
+                    config->bConfigurationValue, configName, config->bmAttributes,
+                    config->bMaxPower);
+
+            env->DeleteLocalRef(configName);
+            free(name);
+        } else if (desc->bDescriptorType == USB_DT_INTERFACE) {
+            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+            char *name = usb_device_get_string(device, interface->iInterface,
+                    USB_CONTROL_TRANSFER_TIMEOUT_MS);
+            jstring interfaceName = AndroidRuntime::NewStringLatin1(env, name);
+
+            env->CallVoidMethod(thiz, method_addUsbInterface,
+                    interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting,
+                    interface->bInterfaceClass, interface->bInterfaceSubClass,
+                    interface->bInterfaceProtocol);
+
+            env->DeleteLocalRef(interfaceName);
+            free(name);
+        } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
+            struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
+
+            env->CallVoidMethod(thiz, method_addUsbEndpoint,
+                    endpoint->bEndpointAddress, endpoint->bmAttributes,
+                    __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval);
+        }
     }
 
+    env->CallVoidMethod(thiz, method_endUsbDeviceAdded);
+
+fail:
     usb_device_close(device);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
 
     return 0;
 }
 
-static int usb_device_removed(const char *devAddress, void* clientData) {
+static int usb_device_removed(const char *devname, void* client_data) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    jobject thiz = (jobject)clientData;
+    jobject thiz = (jobject)client_data;
 
-    jstring deviceAddress = env->NewStringUTF(devAddress);
-    env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
-    env->DeleteLocalRef(deviceAddress);
+    jstring deviceName = env->NewStringUTF(devname);
+    env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName);
+    env->DeleteLocalRef(deviceName);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return 0;
 }
@@ -115,11 +168,11 @@
 }
 
 static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */,
-                                                        jstring deviceAddress)
+                                                        jstring deviceName)
 {
-    const char *deviceAddressStr = env->GetStringUTFChars(deviceAddress, NULL);
-    struct usb_device* device = usb_device_open(deviceAddressStr);
-    env->ReleaseStringUTFChars(deviceAddress, deviceAddressStr);
+    const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+    struct usb_device* device = usb_device_open(deviceNameStr);
+    env->ReleaseStringUTFChars(deviceName, deviceNameStr);
 
     if (!device)
         return NULL;
@@ -153,12 +206,34 @@
         ALOGE("Can't find com/android/server/usb/UsbHostManager");
         return -1;
     }
-    method_usbDeviceAdded =
-            env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;II[B)Z");
-    if (method_usbDeviceAdded == NULL) {
+    method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded",
+            "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;ILjava/lang/String;)Z");
+    if (method_beginUsbDeviceAdded == NULL) {
         ALOGE("Can't find beginUsbDeviceAdded");
         return -1;
     }
+    method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration",
+            "(ILjava/lang/String;II)V");
+    if (method_addUsbConfiguration == NULL) {
+        ALOGE("Can't find addUsbConfiguration");
+        return -1;
+    }
+    method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface",
+            "(ILjava/lang/String;IIII)V");
+    if (method_addUsbInterface == NULL) {
+        ALOGE("Can't find addUsbInterface");
+        return -1;
+    }
+    method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V");
+    if (method_addUsbEndpoint == NULL) {
+        ALOGE("Can't find addUsbEndpoint");
+        return -1;
+    }
+    method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V");
+    if (method_endUsbDeviceAdded == NULL) {
+        ALOGE("Can't find endUsbDeviceAdded");
+        return -1;
+    }
     method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved",
             "(Ljava/lang/String;)V");
     if (method_usbDeviceRemoved == NULL) {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 1f1324a..daf3f2f 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1207,6 +1207,13 @@
         ALOGE("Unable to initialize GNSS NI interface\n");
     }
 
+    sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
+    if (agnssRilIface != nullptr) {
+        agnssRilIface->setCallback(aGnssRilCbIface);
+    } else {
+        ALOGI("Unable to Initialize AGnss Ril interface\n");
+    }
+
     return JNI_TRUE;
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 0085931..0aaf32c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -107,7 +107,8 @@
             return false;
         }
         try {
-           if (mIpConnectivityMetrics.registerNetdEventCallback(mNetdEventCallback)) {
+           if (mIpConnectivityMetrics.addNetdEventCallback(
+                   INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY, mNetdEventCallback)) {
                 mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
                         /* allowIo */ false);
                 mHandlerThread.start();
@@ -138,7 +139,8 @@
                 // logging is forcefully disabled even if unregistering fails
                 return true;
             }
-            return mIpConnectivityMetrics.unregisterNetdEventCallback();
+            return mIpConnectivityMetrics.removeNetdEventCallback(
+                    INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY);
         } catch (RemoteException re) {
             Slog.wtf(TAG, "Failed to make remote calls to unregister the callback", re);
             return true;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f8bcb73..74a7bd4a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -37,7 +37,6 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.os.SystemClock;
@@ -52,7 +51,7 @@
 import android.view.WindowManager;
 
 import com.android.internal.R;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BinderInternal;
@@ -69,7 +68,7 @@
 import com.android.server.coverage.CoverageService;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
 import com.android.server.display.DisplayManagerService;
-import com.android.server.display.NightDisplayService;
+import com.android.server.display.ColorDisplayService;
 import com.android.server.dreams.DreamManagerService;
 import com.android.server.emergency.EmergencyAffordanceService;
 import com.android.server.fingerprint.FingerprintService;
@@ -83,6 +82,7 @@
 import com.android.server.media.projection.MediaProjectionManagerService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
+import com.android.server.net.watchlist.NetworkWatchlistService;
 import com.android.server.notification.NotificationManagerService;
 import com.android.server.oemlock.OemLockService;
 import com.android.server.om.OverlayManagerService;
@@ -95,6 +95,7 @@
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.ShortcutService;
 import com.android.server.pm.UserManagerService;
+import com.android.server.pm.crossprofile.CrossProfileAppsService;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
@@ -884,6 +885,10 @@
             mSystemServiceManager.startService(IpConnectivityMetrics.class);
             traceEnd();
 
+            traceBeginAndSlog("NetworkWatchlistService");
+            mSystemServiceManager.startService(NetworkWatchlistService.Lifecycle.class);
+            traceEnd();
+
             traceBeginAndSlog("PinnerService");
             mSystemServiceManager.startService(PinnerService.class);
             traceEnd();
@@ -1277,9 +1282,9 @@
             mSystemServiceManager.startService(TwilightService.class);
             traceEnd();
 
-            if (NightDisplayController.isAvailable(context)) {
+            if (ColorDisplayController.isAvailable(context)) {
                 traceBeginAndSlog("StartNightDisplay");
-                mSystemServiceManager.startService(NightDisplayService.class);
+                mSystemServiceManager.startService(ColorDisplayService.class);
                 traceEnd();
             }
 
@@ -1485,6 +1490,10 @@
             traceBeginAndSlog("StartLauncherAppsService");
             mSystemServiceManager.startService(LauncherAppsService.class);
             traceEnd();
+
+            traceBeginAndSlog("StartCrossProfileAppsService");
+            mSystemServiceManager.startService(CrossProfileAppsService.class);
+            traceEnd();
         }
 
         if (!disableMediaProjection) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 58833be..e8ae020 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -597,6 +597,7 @@
                 PrintJobStateChangeListenerRecord record =
                         mPrintJobStateChangeListenerRecords.get(i);
                 if (record.listener.asBinder().equals(listener.asBinder())) {
+                    record.destroy();
                     mPrintJobStateChangeListenerRecords.remove(i);
                     break;
                 }
@@ -639,6 +640,7 @@
                 ListenerRecord<IPrintServicesChangeListener> record =
                         mPrintServicesChangeListenerRecords.get(i);
                 if (record.listener.asBinder().equals(listener.asBinder())) {
+                    record.destroy();
                     mPrintServicesChangeListenerRecords.remove(i);
                     break;
                 }
@@ -686,6 +688,7 @@
                 ListenerRecord<IRecommendationsChangeListener> record =
                         mPrintServiceRecommendationsChangeListenerRecords.get(i);
                 if (record.listener.asBinder().equals(listener.asBinder())) {
+                    record.destroy();
                     mPrintServiceRecommendationsChangeListenerRecords.remove(i);
                     break;
                 }
@@ -1285,6 +1288,10 @@
             listener.asBinder().linkToDeath(this, 0);
         }
 
+        public void destroy() {
+            listener.asBinder().unlinkToDeath(this, 0);
+        }
+
         @Override
         public void binderDied() {
             listener.asBinder().unlinkToDeath(this, 0);
@@ -1302,6 +1309,10 @@
             listener.asBinder().linkToDeath(this, 0);
         }
 
+        public void destroy() {
+            listener.asBinder().unlinkToDeath(this, 0);
+        }
+
         @Override
         public void binderDied() {
             listener.asBinder().unlinkToDeath(this, 0);
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 322b891..f63d1e7 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,13 +26,8 @@
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    frameworks-base-testutils \
     services.backup \
-    services.core \
-    android-support-test \
-    mockito-target-minus-junit4 \
-    platform-test-annotations \
-    truth-prebuilt
+    services.core
 
 include $(BUILD_PACKAGE)
 
@@ -45,8 +40,12 @@
 
 # Include the testing libraries (JUnit4 + Robolectric libs).
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    mockito-robolectric-prebuilt \
+    platform-test-annotations \
     truth-prebuilt
 
+# TODO(b/69254249): Migrate to Robolectric 3.4.2
 LOCAL_JAVA_LIBRARIES := \
     junit \
     platform-robolectric-prebuilt
@@ -74,4 +73,5 @@
 
 LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java
 
-include prebuilts/misc/common/robolectric/run_robotests.mk
+# TODO(b/69254249): Migrate to Robolectric 3.4.2
+include prebuilts/misc/common/robolectric/3.1.1/run_robotests.mk
diff --git a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
index cbe9650..8ac6481 100644
--- a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,7 +17,7 @@
 package com.android.server.notification;
 
 import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertTrue;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -100,5 +100,34 @@
                 AudioAttributes.USAGE_GAME);
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
                 AudioAttributes.USAGE_ASSISTANCE_SONIFICATION);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+                AudioAttributes.USAGE_UNKNOWN);
+    }
+
+    @Test
+    public void testZenAllCannotBypass() {
+        // Only audio attributes with SUPPRESIBLE_NEVER can bypass
+        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mConfig.allowAlarms = false;
+        mZenModeHelperSpy.mConfig.allowMediaSystemOther = false;
+        mZenModeHelperSpy.mConfig.allowReminders = false;
+        mZenModeHelperSpy.mConfig.allowCalls = false;
+        mZenModeHelperSpy.mConfig.allowMessages = false;
+        mZenModeHelperSpy.mConfig.allowEvents = false;
+        mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+        assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
+        assertFalse(mZenModeHelperSpy.mConfig.allowMediaSystemOther);
+        assertFalse(mZenModeHelperSpy.mConfig.allowReminders);
+        assertFalse(mZenModeHelperSpy.mConfig.allowCalls);
+        assertFalse(mZenModeHelperSpy.mConfig.allowMessages);
+        assertFalse(mZenModeHelperSpy.mConfig.allowEvents);
+        assertFalse(mZenModeHelperSpy.mConfig.allowRepeatCallers);
+        mZenModeHelperSpy.applyRestrictions();
+
+        for (int usage : AudioAttributes.SDK_USAGES) {
+            boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage)
+                    != AudioAttributes.SUPPRESSIBLE_NEVER;
+            verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
+        }
     }
 }
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 8e41a55..a2ec234 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -25,7 +25,8 @@
     mockito-target-minus-junit4 \
     platform-test-annotations \
     ShortcutManagerTestUtils \
-    truth-prebuilt
+    truth-prebuilt \
+    testng
 
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
 
diff --git a/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml
new file mode 100644
index 0000000..bb97e94
--- /dev/null
+++ b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml
@@ -0,0 +1,27 @@
+<?xml version='1.0'?>
+<watchlist-settings>
+    <sha256-domain>
+        <!-- test-cc-domain.com -->
+        <hash>8E7DCD2AEB4F364358242BB3F403263E61E3B4AECE4E2500FF28BF32E52FF0F1</hash>
+        <!-- test-cc-match-sha256-only.com -->
+        <hash>F0905DA7549614957B449034C281EF7BDEFDBC2B6E050AD1E78D6DE18FBD0D5F</hash>
+    </sha256-domain>
+    <sha256-ip>
+        <!-- 127.0.0.2 -->
+        <hash>1EDD62868F2767A1FFF68DF0A4CB3C23448E45100715768DB9310B5E719536A1</hash>
+        <!-- 127.0.0.3, match in sha256 only -->
+        <hash>18DD41C9F2E8E4879A1575FB780514EF33CF6E1F66578C4AE7CCA31F49B9F2ED</hash>
+    </sha256-ip>
+    <crc32-domain>
+        <!-- test-cc-domain.com -->
+        <hash>6C67059D</hash>
+        <!-- test-cc-match-crc32-only.com -->
+        <hash>3DC775F8</hash>
+    </crc32-domain>
+    <crc32-ip>
+        <!-- 127.0.0.2 -->
+        <hash>4EBEB612</hash>
+        <!-- 127.0.0.4, match in crc32 only -->
+        <hash>A7DD1327</hash>
+    </crc32-ip>
+</watchlist-settings>
diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
rename to services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
index 3a92d63..46b364c 100644
--- a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
@@ -29,10 +29,10 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
 
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.display.DisplayTransformManager;
-import com.android.server.display.NightDisplayService;
+import com.android.server.display.ColorDisplayService;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
@@ -55,15 +55,15 @@
 import static org.mockito.Mockito.doReturn;
 
 @RunWith(AndroidJUnit4.class)
-public class NightDisplayServiceTest {
+public class ColorDisplayServiceTest {
 
     private Context mContext;
     private int mUserId;
 
     private MockTwilightManager mTwilightManager;
 
-    private NightDisplayController mNightDisplayController;
-    private NightDisplayService mNightDisplayService;
+    private ColorDisplayController mColorDisplayController;
+    private ColorDisplayService mColorDisplayService;
 
     @Before
     public void setUp() {
@@ -85,8 +85,8 @@
         mTwilightManager = new MockTwilightManager();
         LocalServices.addService(TwilightManager.class, mTwilightManager);
 
-        mNightDisplayController = new NightDisplayController(mContext, mUserId);
-        mNightDisplayService = new NightDisplayService(mContext);
+        mColorDisplayController = new ColorDisplayController(mContext, mUserId);
+        mColorDisplayService = new ColorDisplayService(mContext);
     }
 
     @After
@@ -94,8 +94,8 @@
         LocalServices.removeServiceForTest(DisplayTransformManager.class);
         LocalServices.removeServiceForTest(TwilightManager.class);
 
-        mNightDisplayService = null;
-        mNightDisplayController = null;
+        mColorDisplayService = null;
+        mColorDisplayController = null;
 
         mTwilightManager = null;
 
@@ -902,9 +902,9 @@
      * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
      */
     private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) {
-        mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_CUSTOM);
-        mNightDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
-        mNightDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
+        mColorDisplayController.setAutoMode(ColorDisplayController.AUTO_MODE_CUSTOM);
+        mColorDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
+        mColorDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
     }
 
     /**
@@ -914,7 +914,7 @@
      * @param sunriseOffset the offset relative to now for sunrise (in minutes)
      */
     private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) {
-        mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_TWILIGHT);
+        mColorDisplayController.setAutoMode(ColorDisplayController.AUTO_MODE_TWILIGHT);
         mTwilightManager.setTwilightState(
                 getTwilightStateRelativeToNow(sunsetOffset, sunriseOffset));
     }
@@ -927,7 +927,7 @@
      * activated (in minutes)
      */
     private void setActivated(boolean activated, int lastActivatedTimeOffset) {
-        mNightDisplayController.setActivated(activated);
+        mColorDisplayController.setActivated(activated);
         Secure.putStringForUser(mContext.getContentResolver(),
                 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
                 LocalDateTime.now().plusMinutes(lastActivatedTimeOffset).toString(),
@@ -935,7 +935,7 @@
     }
 
     /**
-     * Convenience method to start {@link #mNightDisplayService}.
+     * Convenience method to start {@link #mColorDisplayService}.
      */
     private void startService() {
         Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId);
@@ -943,9 +943,9 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mNightDisplayService.onStart();
-                mNightDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
-                mNightDisplayService.onStartUser(mUserId);
+                mColorDisplayService.onStart();
+                mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+                mColorDisplayService.onStartUser(mUserId);
             }
         });
     }
@@ -957,7 +957,7 @@
      */
     private void assertActivated(boolean activated) {
         assertWithMessage("Invalid Night display activated state")
-                .that(mNightDisplayController.isActivated())
+                .that(mColorDisplayController.isActivated())
                 .isEqualTo(activated);
     }
 
@@ -988,21 +988,21 @@
         final LocalDateTime now = LocalDateTime.now();
         final ZoneId zoneId = ZoneId.systemDefault();
 
-        long sunsetMillis = NightDisplayService.getDateTimeBefore(sunset, now)
+        long sunsetMillis = ColorDisplayService.getDateTimeBefore(sunset, now)
                 .atZone(zoneId)
                 .toInstant()
                 .toEpochMilli();
-        long sunriseMillis = NightDisplayService.getDateTimeBefore(sunrise, now)
+        long sunriseMillis = ColorDisplayService.getDateTimeBefore(sunrise, now)
                 .atZone(zoneId)
                 .toInstant()
                 .toEpochMilli();
         if (sunsetMillis < sunriseMillis) {
-            sunsetMillis = NightDisplayService.getDateTimeAfter(sunset, now)
+            sunsetMillis = ColorDisplayService.getDateTimeAfter(sunset, now)
                     .atZone(zoneId)
                     .toInstant()
                     .toEpochMilli();
         } else {
-            sunriseMillis = NightDisplayService.getDateTimeAfter(sunrise, now)
+            sunriseMillis = ColorDisplayService.getDateTimeAfter(sunrise, now)
                     .atZone(zoneId)
                     .toInstant()
                     .toEpochMilli();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 9d23fe9..6de3395 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3193,7 +3193,7 @@
         // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
         // feature is disabled because there are non-affiliated secondary users.
         getServices().removeUser(DpmMockContext.CALLER_USER_HANDLE);
-        when(getServices().iipConnectivityMetrics.registerNetdEventCallback(anyObject()))
+        when(getServices().iipConnectivityMetrics.addNetdEventCallback(anyInt(), anyObject()))
                 .thenReturn(true);
 
         // No logs were retrieved so far.
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 31ed8ba..bf912dd 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -1,50 +1,93 @@
 package com.android.server.job;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
-import android.content.ComponentName;
-import android.content.Context;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import android.app.job.JobInfo;
 import android.app.job.JobInfo.Builder;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.NetworkRequest;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.SystemClock;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
 import android.test.RenamingDelegatingContext;
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.util.HexDump;
+import com.android.server.IoThread;
 import com.android.server.job.JobStore.JobSet;
 import com.android.server.job.controllers.JobStatus;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.time.ZoneOffset;
+import java.util.Arrays;
 import java.util.Iterator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Test reading and writing correctly from file.
  */
-public class JobStoreTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class JobStoreTest {
     private static final String TAG = "TaskStoreTest";
     private static final String TEST_PREFIX = "_test_";
 
-    private static final int SOME_UID = 34234;
+    private static final int SOME_UID = android.os.Process.FIRST_APPLICATION_UID;
     private ComponentName mComponent;
-    private static final long IO_WAIT = 1000L;
 
     JobStore mTaskStoreUnderTest;
     Context mTestContext;
 
-    @Override
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
     public void setUp() throws Exception {
         mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX);
         Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'");
         mTaskStoreUnderTest =
                 JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir());
         mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName());
+
+        // Freeze the clocks at this moment in time
+        JobSchedulerService.sSystemClock =
+                Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+        JobSchedulerService.sUptimeMillisClock =
+                Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
         mTaskStoreUnderTest.clear();
     }
 
+    private void waitForPendingIo() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        IoThread.getHandler().post(() -> {
+            latch.countDown();
+        });
+        latch.await(10, TimeUnit.SECONDS);
+    }
+
+    @Test
     public void testMaybeWriteStatusToDisk() throws Exception {
         int taskId = 5;
         long runByMillis = 20000L; // 20s
@@ -61,7 +104,8 @@
                 .build();
         final JobStatus ts = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null);
         mTaskStoreUnderTest.add(ts);
-        Thread.sleep(IO_WAIT);
+        waitForPendingIo();
+
         // Manually load tasks from xml file.
         final JobSet jobStatusSet = new JobSet();
         mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -75,9 +119,9 @@
                 ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
         compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
                 ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed());
-
     }
 
+    @Test
     public void testWritingTwoFilesToDisk() throws Exception {
         final JobInfo task1 = new Builder(8, mComponent)
                 .setRequiresDeviceIdle(true)
@@ -96,7 +140,7 @@
         final JobStatus taskStatus2 = JobStatus.createFromJobInfo(task2, SOME_UID, null, -1, null);
         mTaskStoreUnderTest.add(taskStatus1);
         mTaskStoreUnderTest.add(taskStatus2);
-        Thread.sleep(IO_WAIT);
+        waitForPendingIo();
 
         final JobSet jobStatusSet = new JobSet();
         mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -125,9 +169,9 @@
                 taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime());
         compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
                 taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed());
-
     }
 
+    @Test
     public void testWritingTaskWithExtras() throws Exception {
         JobInfo.Builder b = new Builder(8, mComponent)
                 .setRequiresDeviceIdle(true)
@@ -144,7 +188,7 @@
         JobStatus taskStatus = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null);
 
         mTaskStoreUnderTest.add(taskStatus);
-        Thread.sleep(IO_WAIT);
+        waitForPendingIo();
 
         final JobSet jobStatusSet = new JobSet();
         mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -152,6 +196,8 @@
         JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
         assertTasksEqual(task, loaded.getJob());
     }
+
+    @Test
     public void testWritingTaskWithSourcePackage() throws Exception {
         JobInfo.Builder b = new Builder(8, mComponent)
                 .setRequiresDeviceIdle(true)
@@ -162,7 +208,7 @@
                 "com.google.android.gms", 0, null);
 
         mTaskStoreUnderTest.add(taskStatus);
-        Thread.sleep(IO_WAIT);
+        waitForPendingIo();
 
         final JobSet jobStatusSet = new JobSet();
         mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -174,6 +220,7 @@
                 taskStatus.getSourceUserId());
     }
 
+    @Test
     public void testWritingTaskWithFlex() throws Exception {
         JobInfo.Builder b = new Builder(8, mComponent)
                 .setRequiresDeviceIdle(true)
@@ -183,7 +230,7 @@
         JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
 
         mTaskStoreUnderTest.add(taskStatus);
-        Thread.sleep(IO_WAIT);
+        waitForPendingIo();
 
         final JobSet jobStatusSet = new JobSet();
         mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -195,6 +242,7 @@
                 taskStatus.getJob().getFlexMillis());
     }
 
+    @Test
     public void testMassivePeriodClampedOnRead() throws Exception {
         final long ONE_HOUR = 60*60*1000L; // flex
         final long TWO_HOURS = 2 * ONE_HOUR; // period
@@ -214,7 +262,7 @@
                 persistedExecutionTimesUTC);
 
         mTaskStoreUnderTest.add(js);
-        Thread.sleep(IO_WAIT);
+        waitForPendingIo();
 
         final JobSet jobStatusSet = new JobSet();
         mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -231,6 +279,7 @@
                 loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS + ONE_HOUR);
     }
 
+    @Test
     public void testPriorityPersisted() throws Exception {
         JobInfo.Builder b = new Builder(92, mComponent)
                 .setOverrideDeadline(5000)
@@ -238,7 +287,8 @@
                 .setPersisted(true);
         final JobStatus js = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
         mTaskStoreUnderTest.add(js);
-        Thread.sleep(IO_WAIT);
+        waitForPendingIo();
+
         final JobSet jobStatusSet = new JobSet();
         mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
         JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
@@ -248,6 +298,7 @@
     /**
      * Test that non persisted job is not written to disk.
      */
+    @Test
     public void testNonPersistedTaskIsNotPersisted() throws Exception {
         JobInfo.Builder b = new Builder(42, mComponent)
                 .setOverrideDeadline(10000)
@@ -259,7 +310,8 @@
                 .setPersisted(true);
         JobStatus jsPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
         mTaskStoreUnderTest.add(jsPersisted);
-        Thread.sleep(IO_WAIT);
+        waitForPendingIo();
+
         final JobSet jobStatusSet = new JobSet();
         mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
         assertEquals("Job count is incorrect.", 1, jobStatusSet.size());
@@ -267,6 +319,62 @@
         assertEquals("Wrong job persisted.", 43, jobStatus.getJobId());
     }
 
+    @Test
+    public void testRequiredNetworkType() throws Exception {
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiresDeviceIdle(true)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE).build());
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build());
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED).build());
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING).build());
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR).build());
+    }
+
+    @Test
+    public void testRequiredNetwork() throws Exception {
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiresDeviceIdle(true)
+                .setRequiredNetwork(null).build());
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiredNetwork(new NetworkRequest.Builder().build()).build());
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiredNetwork(new NetworkRequest.Builder()
+                        .addTransportType(TRANSPORT_WIFI).build())
+                .build());
+        assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+                .setPersisted(true)
+                .setRequiredNetwork(new NetworkRequest.Builder()
+                        .addCapability(NET_CAPABILITY_IMS).build())
+                .build());
+    }
+
+    /**
+     * Helper function to kick a {@link JobInfo} through a persistence cycle and
+     * assert that it's unchanged.
+     */
+    private void assertPersistedEquals(JobInfo first) throws Exception {
+        mTaskStoreUnderTest.clear();
+        mTaskStoreUnderTest.add(JobStatus.createFromJobInfo(first, SOME_UID, null, -1, null));
+        waitForPendingIo();
+
+        final JobSet jobStatusSet = new JobSet();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+        final JobStatus second = jobStatusSet.getAllJobs().iterator().next();
+        assertTasksEqual(first, second.getJob());
+    }
+
     /**
      * Helper function to throw an error if the provided task and TaskStatus objects are not equal.
      */
@@ -286,12 +394,10 @@
                 second.isRequireBatteryNotLow());
         assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(),
                 second.isRequireDeviceIdle());
-        assertEquals("Invalid unmetered constraint.",
-                first.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED,
-                second.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED);
-        assertEquals("Invalid connectivity constraint.",
-                first.getNetworkType() == JobInfo.NETWORK_TYPE_ANY,
-                second.getNetworkType() == JobInfo.NETWORK_TYPE_ANY);
+        assertEquals("Invalid network type.",
+                first.getNetworkType(), second.getNetworkType());
+        assertEquals("Invalid network.",
+                first.getRequiredNetwork(), second.getRequiredNetwork());
         assertEquals("Invalid deadline constraint.",
                 first.hasLateConstraint(),
                 second.hasLateConstraint());
@@ -302,6 +408,26 @@
                 first.getExtras().toString(), second.getExtras().toString());
         assertEquals("Transient xtras don't match",
                 first.getTransientExtras().toString(), second.getTransientExtras().toString());
+
+        // Since people can forget to add tests here for new fields, do one last
+        // sanity check based on bits-on-wire equality.
+        final byte[] firstBytes = marshall(first);
+        final byte[] secondBytes = marshall(second);
+        if (!Arrays.equals(firstBytes, secondBytes)) {
+            Log.w(TAG, "First: " + HexDump.dumpHexString(firstBytes));
+            Log.w(TAG, "Second: " + HexDump.dumpHexString(secondBytes));
+            fail("Raw JobInfo aren't equal; see logs for details");
+        }
+    }
+
+    private static byte[] marshall(Parcelable p) {
+        final Parcel parcel = Parcel.obtain();
+        try {
+            p.writeToParcel(parcel, 0);
+            return parcel.marshall();
+        } finally {
+            parcel.recycle();
+        }
     }
 
     /**
@@ -312,7 +438,7 @@
      */
     private void compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2) {
         final long DELTA_MILLIS = 700L;  // We allow up to 700ms of latency for IO read/writes.
-        assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS + IO_WAIT);
+        assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS);
     }
 
     private static class StubClass {}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java
new file mode 100644
index 0000000..a34f95e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java
@@ -0,0 +1,63 @@
+/*
+ * 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.net.watchlist;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.HarmfulDigestsTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class HarmfulDigestsTests {
+
+    private static final byte[] TEST_DIGEST_1 = HexDump.hexStringToByteArray("AAAAAA");
+    private static final byte[] TEST_DIGEST_2 = HexDump.hexStringToByteArray("BBBBBB");
+    private static final byte[] TEST_DIGEST_3 = HexDump.hexStringToByteArray("AAAABB");
+    private static final byte[] TEST_DIGEST_4 = HexDump.hexStringToByteArray("BBBBAA");
+
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void testHarmfulDigests_setAndContains() throws Exception {
+        HarmfulDigests harmfulDigests = new HarmfulDigests(
+                Arrays.asList(new byte[][] {TEST_DIGEST_1}));
+        assertTrue(harmfulDigests.contains(TEST_DIGEST_1));
+        assertFalse(harmfulDigests.contains(TEST_DIGEST_2));
+        assertFalse(harmfulDigests.contains(TEST_DIGEST_3));
+        assertFalse(harmfulDigests.contains(TEST_DIGEST_4));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
new file mode 100644
index 0000000..ccd3cdd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
@@ -0,0 +1,217 @@
+/*
+ * 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.net.watchlist;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.ConnectivityMetricsEvent;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.ServiceThread;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.NetworkWatchlistServiceTests
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class NetworkWatchlistServiceTests {
+
+    private static final long NETWOR_EVENT_TIMEOUT_SEC = 1;
+    private static final String TEST_HOST = "testhost.com";
+    private static final String TEST_IP = "7.6.8.9";
+    private static final String[] TEST_IPS =
+            new String[] {"1.2.3.4", "4.6.8.9", "2001:0db8:0001:0000:0000:0ab9:C0A8:0102"};
+
+    private static class TestHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case WatchlistLoggingHandler.LOG_WATCHLIST_EVENT_MSG:
+                    onLogEvent();
+                    break;
+                case WatchlistLoggingHandler.REPORT_RECORDS_IF_NECESSARY_MSG:
+                    onAggregateEvent();
+                    break;
+                default:
+                    fail("Unexpected message: " + msg.what);
+            }
+        }
+
+        public void onLogEvent() {}
+        public void onAggregateEvent() {}
+    }
+
+    private static class TestIIpConnectivityMetrics implements IIpConnectivityMetrics {
+
+        int counter = 0;
+        INetdEventCallback callback = null;
+
+        @Override
+        public IBinder asBinder() {
+            return null;
+        }
+
+        @Override
+        public int logEvent(ConnectivityMetricsEvent connectivityMetricsEvent)
+                    throws RemoteException {
+            return 0;
+        }
+
+        @Override
+        public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+            counter++;
+            this.callback = callback;
+            return true;
+        }
+
+        @Override
+        public boolean removeNetdEventCallback(int callerType) {
+            counter--;
+            return true;
+        }
+    };
+
+    ServiceThread mHandlerThread;
+    WatchlistLoggingHandler mWatchlistHandler;
+    NetworkWatchlistService mWatchlistService;
+
+    @Before
+    public void setUp() {
+        mHandlerThread = new ServiceThread("NetworkWatchlistServiceTests",
+                Process.THREAD_PRIORITY_BACKGROUND, /* allowIo */ false);
+        mHandlerThread.start();
+        mWatchlistHandler = new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+                mHandlerThread.getLooper());
+        mWatchlistService = new NetworkWatchlistService(InstrumentationRegistry.getContext(),
+                mHandlerThread, mWatchlistHandler, null);
+    }
+
+    @After
+    public void tearDown() {
+        mHandlerThread.quitSafely();
+    }
+
+    @Test
+    public void testStartStopWatchlistLogging() throws Exception {
+        TestIIpConnectivityMetrics connectivityMetrics = new TestIIpConnectivityMetrics() {
+            @Override
+            public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+                super.addNetdEventCallback(callerType, callback);
+                assertEquals(callerType, INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+                return true;
+            }
+
+            @Override
+            public boolean removeNetdEventCallback(int callerType) {
+                super.removeNetdEventCallback(callerType);
+                assertEquals(callerType, INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+                return true;
+            }
+        };
+        assertEquals(connectivityMetrics.counter, 0);
+        mWatchlistService.mIpConnectivityMetrics = connectivityMetrics;
+        assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+        assertEquals(connectivityMetrics.counter, 1);
+        assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+        assertEquals(connectivityMetrics.counter, 1);
+        assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+        assertEquals(connectivityMetrics.counter, 0);
+        assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+        assertEquals(connectivityMetrics.counter, 0);
+        assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+        assertEquals(connectivityMetrics.counter, 1);
+        assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+        assertEquals(connectivityMetrics.counter, 0);
+    }
+
+    @Test
+    public void testNetworkEvents() throws Exception {
+        TestIIpConnectivityMetrics connectivityMetrics = new TestIIpConnectivityMetrics();
+        mWatchlistService.mIpConnectivityMetrics = connectivityMetrics;
+        assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+
+        // Test DNS events
+        final CountDownLatch testDnsLatch = new CountDownLatch(1);
+        final Object[] dnsParams = new Object[3];
+        final WatchlistLoggingHandler testDnsHandler =
+                new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+                        mHandlerThread.getLooper()) {
+                    @Override
+                    public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+                        dnsParams[0] = host;
+                        dnsParams[1] = ipAddresses;
+                        dnsParams[2] = uid;
+                        testDnsLatch.countDown();
+                    }
+                };
+        mWatchlistService.mNetworkWatchlistHandler = testDnsHandler;
+        connectivityMetrics.callback.onDnsEvent(TEST_HOST, TEST_IPS, TEST_IPS.length, 123L, 456);
+        if (!testDnsLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for network event");
+        }
+        assertEquals(TEST_HOST, dnsParams[0]);
+        for (int i = 0; i < TEST_IPS.length; i++) {
+            assertEquals(TEST_IPS[i], ((String[])dnsParams[1])[i]);
+        }
+        assertEquals(456, dnsParams[2]);
+
+        // Test connect events
+        final CountDownLatch testConnectLatch = new CountDownLatch(1);
+        final Object[] connectParams = new Object[3];
+        final WatchlistLoggingHandler testConnectHandler =
+                new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+                        mHandlerThread.getLooper()) {
+                    @Override
+                    public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+                        connectParams[0] = host;
+                        connectParams[1] = ipAddresses;
+                        connectParams[2] = uid;
+                        testConnectLatch.countDown();
+                    }
+                };
+        mWatchlistService.mNetworkWatchlistHandler = testConnectHandler;
+        connectivityMetrics.callback.onConnectEvent(TEST_IP, 80, 123L, 456);
+        if (!testConnectLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for network event");
+        }
+        assertNull(connectParams[0]);
+        assertEquals(1, ((String[]) connectParams[1]).length);
+        assertEquals(TEST_IP, ((String[]) connectParams[1])[0]);
+        assertEquals(456, connectParams[2]);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
new file mode 100644
index 0000000..e356b13
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
@@ -0,0 +1,61 @@
+/*
+ * 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.net.watchlist;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+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.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistLoggingHandlerTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WatchlistLoggingHandlerTests {
+
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void testWatchlistLoggingHandler_getAllSubDomains() throws Exception {
+        String[] subDomains = WatchlistLoggingHandler.getAllSubDomains("abc.def.gh.i.jkl.mm");
+        assertTrue(Arrays.equals(subDomains, new String[] {"abc.def.gh.i.jkl.mm",
+                "def.gh.i.jkl.mm", "gh.i.jkl.mm", "i.jkl.mm", "jkl.mm", "mm"}));
+        subDomains = WatchlistLoggingHandler.getAllSubDomains(null);
+        assertNull(subDomains);
+        subDomains = WatchlistLoggingHandler.getAllSubDomains("jkl.mm");
+        assertTrue(Arrays.equals(subDomains, new String[] {"jkl.mm", "mm"}));
+        subDomains = WatchlistLoggingHandler.getAllSubDomains("abc");
+        assertTrue(Arrays.equals(subDomains, new String[] {"abc"}));
+        subDomains = WatchlistLoggingHandler.getAllSubDomains("jkl.mm.");
+        assertTrue(Arrays.equals(subDomains, new String[] {"jkl.mm.", "mm."}));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
new file mode 100644
index 0000000..f3cb980
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
@@ -0,0 +1,195 @@
+/*
+ * 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.net.watchlist;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistSettingsTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WatchlistSettingsTests {
+
+    private static final String TEST_XML_1 = "NetworkWatchlistTest/watchlist_settings_test1.xml";
+    private static final String TEST_CC_DOMAIN = "test-cc-domain.com";
+    private static final String TEST_CC_IP = "127.0.0.2";
+    private static final String TEST_NOT_EXIST_CC_DOMAIN = "test-not-exist-cc-domain.com";
+    private static final String TEST_NOT_EXIST_CC_IP = "1.2.3.4";
+    private static final String TEST_SHA256_ONLY_DOMAIN = "test-cc-match-sha256-only.com";
+    private static final String TEST_SHA256_ONLY_IP = "127.0.0.3";
+    private static final String TEST_CRC32_ONLY_DOMAIN = "test-cc-match-crc32-only.com";
+    private static final String TEST_CRC32_ONLY_IP = "127.0.0.4";
+
+    private static final String TEST_NEW_CC_DOMAIN = "test-new-cc-domain.com";
+    private static final byte[] TEST_NEW_CC_DOMAIN_SHA256 = HexDump.hexStringToByteArray(
+            "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43");
+    private static final byte[] TEST_NEW_CC_DOMAIN_CRC32 = HexDump.hexStringToByteArray("76795BD3");
+
+    private static final String TEST_NEW_CC_IP = "1.1.1.2";
+    private static final byte[] TEST_NEW_CC_IP_SHA256 = HexDump.hexStringToByteArray(
+            "721BAB5E313CF0CC76B10F9592F18B9D1B8996497501A3306A55B3AE9F1CC87C");
+    private static final byte[] TEST_NEW_CC_IP_CRC32 = HexDump.hexStringToByteArray("940B8BEE");
+
+    private Context mContext;
+    private File mTestXmlFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mTestXmlFile =  new File(mContext.getFilesDir(), "test_watchlist_settings.xml");
+        mTestXmlFile.delete();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTestXmlFile.delete();
+    }
+
+    @Test
+    public void testWatchlistSettings_parsing() throws Exception {
+        copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+        WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+        assertTrue(settings.containsDomain(TEST_CC_DOMAIN));
+        assertTrue(settings.containsIp(TEST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+        assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+    }
+
+    @Test
+    public void testWatchlistSettings_writeSettingsToDisk() throws Exception {
+        copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+        WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+        settings.writeSettingsToDisk(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32),
+                Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32),
+                Arrays.asList(TEST_NEW_CC_IP_SHA256));
+        // Ensure old watchlist is not in memory
+        assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+        assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+        // Ensure new watchlist is in memory
+        assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+        assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+        // Reload settings from disk and test again
+        settings = new WatchlistSettings(mTestXmlFile);
+        // Ensure old watchlist is not in memory
+        assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+        assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+        // Ensure new watchlist is in memory
+        assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+        assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+    }
+
+    @Test
+    public void testWatchlistSettings_writeSettingsToMemory() throws Exception {
+        copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+        WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+        settings.writeSettingsToMemory(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32),
+                Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32),
+                Arrays.asList(TEST_NEW_CC_IP_SHA256));
+        // Ensure old watchlist is not in memory
+        assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+        assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+        // Ensure new watchlist is in memory
+        assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+        assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+        // Reload settings from disk and test again
+        settings = new WatchlistSettings(mTestXmlFile);
+        // Ensure old watchlist is in memory
+        assertTrue(settings.containsDomain(TEST_CC_DOMAIN));
+        assertTrue(settings.containsIp(TEST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+        assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+        assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+        assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+        // Ensure new watchlist is not in memory
+        assertFalse(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+        assertFalse(settings.containsIp(TEST_NEW_CC_IP));;
+    }
+
+    private static void copyWatchlistSettingsXml(Context context, String xmlAsset, File outFile)
+            throws IOException {
+        writeToFile(outFile, readAsset(context, xmlAsset));
+
+    }
+
+    private static String readAsset(Context context, String assetPath) throws IOException {
+        final StringBuilder sb = new StringBuilder();
+        try (BufferedReader br = new BufferedReader(
+                new InputStreamReader(
+                        context.getResources().getAssets().open(assetPath)))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                sb.append(line);
+                sb.append(System.lineSeparator());
+            }
+        }
+        return sb.toString();
+    }
+
+    private static void writeToFile(File path, String content)
+            throws IOException {
+        path.getParentFile().mkdirs();
+
+        try (FileWriter writer = new FileWriter(path)) {
+            writer.write(content);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index b656d5e..dd9a8ab 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -309,6 +309,8 @@
 
     @MediumTest
     public void testAddRestrictedProfile() throws Exception {
+        assertFalse("There should be no associated restricted profiles before the test",
+                mUserManager.hasRestrictedProfiles());
         UserInfo userInfo = createRestrictedProfile("Profile");
         assertNotNull(userInfo);
 
@@ -324,6 +326,9 @@
                 userInfo.id);
         assertEquals("Restricted profile should have setting LOCATION_MODE set to "
                 + "LOCATION_MODE_OFF by default", locationMode, Settings.Secure.LOCATION_MODE_OFF);
+
+        assertTrue("Newly created profile should be associated with the current user",
+                mUserManager.hasRestrictedProfiles());
     }
 
     @MediumTest
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 480be2e..882bf32 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -89,6 +89,7 @@
         assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_RECORD_AUDIO));
         assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_WALLPAPER));
         assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_ADD_USER));
+        assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_USER_SWITCH));
     }
 
     public void testCanProfileOwnerChange() {
@@ -97,6 +98,8 @@
                 UserManager.DISALLOW_RECORD_AUDIO, user));
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_WALLPAPER, user));
+        assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
+                UserManager.DISALLOW_USER_SWITCH, user));
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_ADD_USER, user));
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
@@ -109,6 +112,8 @@
                 UserManager.DISALLOW_WALLPAPER, user));
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_ADD_USER, user));
+        assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
+                UserManager.DISALLOW_USER_SWITCH, user));
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_ADJUST_VOLUME, user));
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
new file mode 100644
index 0000000..880b77e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
@@ -0,0 +1,449 @@
+package com.android.server.pm.crossprofile;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.pm.crossprofile.CrossProfileAppsServiceImplTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class CrossProfileAppsServiceImplTest {
+    private static final String PACKAGE_ONE = "com.one";
+    private static final int PACKAGE_ONE_UID = 1111;
+    private static final ComponentName ACTIVITY_COMPONENT =
+            new ComponentName("com.one", "test");
+
+    private static final String PACKAGE_TWO = "com.two";
+    private static final int PACKAGE_TWO_UID = 2222;
+
+    private static final int PRIMARY_USER = 0;
+    private static final int PROFILE_OF_PRIMARY_USER = 10;
+    private static final int SECONDARY_USER = 11;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+    @Mock
+    private AppOpsManager mAppOpsManager;
+
+    private TestInjector mTestInjector;
+    private ActivityInfo mActivityInfo;
+    private CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl;
+
+    private SparseArray<Boolean> mUserEnabled = new SparseArray<>();
+
+    @Before
+    public void initCrossProfileAppsServiceImpl() {
+        mTestInjector = new TestInjector();
+        mCrossProfileAppsServiceImpl = new CrossProfileAppsServiceImpl(mContext, mTestInjector);
+    }
+
+    @Before
+    public void setupEnabledProfiles() {
+        mUserEnabled.put(PRIMARY_USER, true);
+        mUserEnabled.put(PROFILE_OF_PRIMARY_USER, true);
+        mUserEnabled.put(SECONDARY_USER, true);
+
+        when(mUserManager.getEnabledProfileIds(anyInt())).thenAnswer(
+                invocation -> {
+                    List<Integer> users = new ArrayList<>();
+                    final int targetUser = invocation.getArgument(0);
+                    users.add(targetUser);
+
+                    int profileUserId = -1;
+                    if (targetUser == PRIMARY_USER) {
+                        profileUserId = PROFILE_OF_PRIMARY_USER;
+                    } else if (targetUser == PROFILE_OF_PRIMARY_USER) {
+                        profileUserId = PRIMARY_USER;
+                    }
+
+                    if (profileUserId != -1 && mUserEnabled.get(profileUserId)) {
+                        users.add(profileUserId);
+                    }
+                    return users.stream().mapToInt(i -> i).toArray();
+                });
+    }
+
+    @Before
+    public void setupCaller() {
+        mTestInjector.setCallingUid(PACKAGE_ONE_UID);
+        mTestInjector.setCallingUserId(PRIMARY_USER);
+    }
+
+    @Before
+    public void setupPackage() throws Exception {
+        // PACKAGE_ONE are installed in all users.
+        mockAppsInstalled(PACKAGE_ONE, PRIMARY_USER, true);
+        mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, true);
+        mockAppsInstalled(PACKAGE_ONE, SECONDARY_USER, true);
+
+        // Packages are resolved to their corresponding UID.
+        doAnswer(invocation -> {
+            final int uid = invocation.getArgument(0);
+            final String packageName = invocation.getArgument(1);
+            if (uid == PACKAGE_ONE_UID && PACKAGE_ONE.equals(packageName)) {
+                return null;
+            } else if (uid ==PACKAGE_TWO_UID && PACKAGE_TWO.equals(packageName)) {
+                return null;
+            }
+            throw new SecurityException("Not matching");
+        }).when(mAppOpsManager).checkPackage(anyInt(), anyString());
+
+        // The intent is resolved to the ACTIVITY_COMPONENT.
+        mockActivityLaunchIntentResolvedTo(ACTIVITY_COMPONENT);
+    }
+
+    @Test
+    public void getTargetUserProfiles_fromPrimaryUser_installed() throws Exception {
+        List<UserHandle> targetProfiles =
+                mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+        assertThat(targetProfiles).containsExactly(UserHandle.of(PROFILE_OF_PRIMARY_USER));
+    }
+
+    @Test
+    public void getTargetUserProfiles_fromPrimaryUser_notInstalled() throws Exception {
+        mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+        List<UserHandle> targetProfiles =
+                mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+        assertThat(targetProfiles).isEmpty();
+    }
+
+    @Test
+    public void getTargetUserProfiles_fromPrimaryUser_userNotEnabled() throws Exception {
+        mUserEnabled.put(PROFILE_OF_PRIMARY_USER, false);
+
+        List<UserHandle> targetProfiles =
+                mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+        assertThat(targetProfiles).isEmpty();
+    }
+
+    @Test
+    public void getTargetUserProfiles_fromSecondaryUser() throws Exception {
+        mTestInjector.setCallingUserId(SECONDARY_USER);
+
+        List<UserHandle> targetProfiles =
+                mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+        assertThat(targetProfiles).isEmpty();
+    }
+
+    @Test
+    public void getTargetUserProfiles_fromProfile_installed() throws Exception {
+        mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+
+        List<UserHandle> targetProfiles =
+                mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+        assertThat(targetProfiles).containsExactly(UserHandle.of(PRIMARY_USER));
+    }
+
+    @Test
+    public void getTargetUserProfiles_fromProfile_notInstalled() throws Exception {
+        mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+        mockAppsInstalled(PACKAGE_ONE, PRIMARY_USER, false);
+
+        List<UserHandle> targetProfiles =
+                mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+        assertThat(targetProfiles).isEmpty();
+    }
+
+    @Test(expected = SecurityException.class)
+    public void getTargetUserProfiles_fakeCaller() throws Exception {
+        mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_TWO);
+    }
+
+    @Test
+    public void startActivityAsUser_currentUser() throws Exception {
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                PACKAGE_ONE,
+                                ACTIVITY_COMPONENT,
+                                null,
+                                null,
+                                UserHandle.of(PRIMARY_USER)));
+
+        verify(mContext, never())
+                .startActivityAsUser(
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        any(UserHandle.class));
+    }
+
+    @Test
+    public void startActivityAsUser_profile_successWithOption() throws Exception {
+        Bundle options = Bundle.forPair("test_key", "test_value");
+
+        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                PACKAGE_ONE,
+                ACTIVITY_COMPONENT,
+                null,
+                options,
+                UserHandle.of(PROFILE_OF_PRIMARY_USER));
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+
+        verify(mContext)
+                .startActivityAsUser(
+                        intentCaptor.capture(),
+                        bundleCaptor.capture(),
+                        eq(UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+        Intent intent = intentCaptor.getValue();
+        assertEquals(ACTIVITY_COMPONENT, intent.getComponent());
+
+        Bundle bundle = bundleCaptor.getValue();
+        assertEquals("test_value", bundle.getString("test_key"));
+    }
+
+    @Test
+    public void startActivityAsUser_profile_notInstalled() throws Exception {
+        mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                PACKAGE_ONE,
+                                ACTIVITY_COMPONENT,
+                                null,
+                                null,
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+        verify(mContext, never())
+                .startActivityAsUser(
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        any(UserHandle.class));
+    }
+
+    @Test
+    public void startActivityAsUser_profile_fakeCaller() throws Exception {
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                PACKAGE_TWO,
+                                ACTIVITY_COMPONENT,
+                                null,
+                                null,
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+        verify(mContext, never())
+                .startActivityAsUser(
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        any(UserHandle.class));
+    }
+
+    @Test
+    public void startActivityAsUser_profile_notExported() throws Exception {
+        mActivityInfo.exported = false;
+
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                PACKAGE_ONE,
+                                ACTIVITY_COMPONENT,
+                                null,
+                                null,
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+        verify(mContext, never())
+                .startActivityAsUser(
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        any(UserHandle.class));
+    }
+
+    @Test
+    public void startActivityAsUser_profile_anotherPackage() throws Exception {
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                PACKAGE_ONE,
+                                new ComponentName(PACKAGE_TWO, "test"),
+                                null,
+                                null,
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+        verify(mContext, never())
+                .startActivityAsUser(
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        any(UserHandle.class));
+    }
+
+    @Test
+    public void startActivityAsUser_secondaryUser() throws Exception {
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                PACKAGE_ONE,
+                                ACTIVITY_COMPONENT,
+                                null,
+                                null,
+                                UserHandle.of(SECONDARY_USER)));
+
+        verify(mContext, never())
+                .startActivityAsUser(
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        any(UserHandle.class));
+    }
+
+    @Test
+    public void startActivityAsUser_fromProfile_success() throws Exception {
+        mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+
+        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                PACKAGE_ONE,
+                ACTIVITY_COMPONENT,
+                null,
+                null,
+                UserHandle.of(PRIMARY_USER));
+
+        verify(mContext)
+                .startActivityAsUser(
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        eq(UserHandle.of(PRIMARY_USER)));
+    }
+
+    private void mockAppsInstalled(String packageName, int user, boolean installed) {
+        when(mPackageManagerInternal.getPackageInfo(
+                eq(packageName),
+                anyInt(),
+                anyInt(),
+                eq(user)))
+                .thenReturn(installed ? createInstalledPackageInfo() : null);
+    }
+
+    private PackageInfo createInstalledPackageInfo() {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.enabled = true;
+        return packageInfo;
+    }
+
+    private void mockActivityLaunchIntentResolvedTo(ComponentName componentName) {
+        ResolveInfo resolveInfo = new ResolveInfo();
+        ActivityInfo activityInfo = new ActivityInfo();
+        activityInfo.packageName = componentName.getPackageName();
+        activityInfo.name = componentName.getClassName();
+        activityInfo.exported = true;
+        resolveInfo.activityInfo = activityInfo;
+        mActivityInfo = activityInfo;
+
+        when(mPackageManagerInternal.queryIntentActivities(
+                any(Intent.class), anyInt(), anyInt(), anyInt()))
+                .thenReturn(Collections.singletonList(resolveInfo));
+    }
+
+    private class TestInjector implements CrossProfileAppsServiceImpl.Injector {
+        private int mCallingUid;
+        private int mCallingUserId;
+
+        public void setCallingUid(int uid) {
+            mCallingUid = uid;
+        }
+
+        public void setCallingUserId(int userId) {
+            mCallingUserId = userId;
+        }
+
+        @Override
+        public int getCallingUid() {
+            return mCallingUid;
+        }
+
+        @Override
+        public int getCallingUserId() {
+            return mCallingUserId;
+        }
+
+        @Override
+        public UserHandle getCallingUserHandle() {
+            return UserHandle.of(mCallingUserId);
+        }
+
+        @Override
+        public long clearCallingIdentity() {
+            return 0;
+        }
+
+        @Override
+        public void restoreCallingIdentity(long token) {
+        }
+
+        @Override
+        public UserManager getUserManager() {
+            return mUserManager;
+        }
+
+        @Override
+        public PackageManagerInternal getPackageManagerInternal() {
+            return mPackageManagerInternal;
+        }
+
+        @Override
+        public PackageManager getPackageManager() {
+            return mPackageManager;
+        }
+
+        @Override
+        public AppOpsManager getAppOpsManager() {
+            return mAppOpsManager;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 67ffe58..39d256a 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,16 +16,14 @@
 
 package com.android.server.usage;
 
-import static android.app.usage.AppStandby.REASON_TIMEOUT;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;
-
 import android.app.usage.AppStandby;
 import android.os.FileUtils;
 import android.test.AndroidTestCase;
 
 import java.io.File;
 
+import static android.app.usage.AppStandby.*;
+
 public class AppIdleHistoryTests extends AndroidTestCase {
 
     File mStorageDir;
@@ -111,5 +109,9 @@
         assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
         assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
+
+        assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
+        assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
+        assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_FREQUENT));
     }
 }
\ No newline at end of file
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 9846d6f..8531baf 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -16,11 +16,7 @@
 
 package com.android.server.usage;
 
-import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_FREQUENT;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_WORKING_SET;
-
+import static android.app.usage.AppStandby.*;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -251,10 +247,22 @@
                         false));
     }
 
+    private void reportEvent(AppStandbyController controller, long elapsedTime) {
+        // Back to ACTIVE on event
+        UsageEvents.Event ev = new UsageEvents.Event();
+        ev.mPackage = PACKAGE_1;
+        ev.mEventType = UsageEvents.Event.USER_INTERACTION;
+        controller.reportEvent(ev, elapsedTime, USER_ID);
+    }
+
     @Test
     public void testBuckets() throws Exception {
         AppStandbyController controller = setupController();
 
+        assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+
+        reportEvent(controller, 0);
+
         // ACTIVE bucket
         assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
 
@@ -270,11 +278,7 @@
         // RARE bucket
         assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);
 
-        // Back to ACTIVE on event
-        UsageEvents.Event ev = new UsageEvents.Event();
-        ev.mPackage = PACKAGE_1;
-        ev.mEventType = UsageEvents.Event.USER_INTERACTION;
-        controller.reportEvent(ev, mInjector.mElapsedRealtime, USER_ID);
+        reportEvent(controller, 9 * DAY_MS);
 
         assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);
 
@@ -287,6 +291,10 @@
         AppStandbyController controller = setupController();
         mInjector.setDisplayOn(false);
 
+        assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+
+        reportEvent(controller, 0);
+
         // ACTIVE bucket
         assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 27c5eab..693264c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -18,10 +18,14 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static org.junit.Assert.assertEquals;
@@ -31,6 +35,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import android.annotation.SuppressLint;
 import android.content.res.Configuration;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
@@ -379,6 +384,31 @@
         assertEquals(-1, orderedDisplayIds.indexOfValue(dc.getDisplayId()));
     }
 
+    @Test
+    @SuppressLint("InlinedApi")
+    public void testOrientationDefinedByKeyguard() {
+        final DisplayContent dc = createNewDisplay();
+        // Create a window that requests landscape orientation. It will define device orientation
+        // by default.
+        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+        window.mAppToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+        final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, dc, "keyguard");
+        keyguard.mHasSurface = true;
+        keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+        assertEquals("Screen orientation must be defined by the app window by default",
+                SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
+
+        keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+        assertEquals("Visible keyguard must influence device orientation",
+                SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
+
+        sWm.setKeyguardGoingAway(true);
+        assertEquals("Keyguard that is going away must not influence device orientation",
+                SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
+    }
+
     private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
                              int expectedBaseHeight, int expectedBaseDensity) {
         assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth);
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 9c80544..f23bd62 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -16,8 +16,12 @@
 
 package com.android.server.wm;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.graphics.Color.BLUE;
 import static android.graphics.Color.RED;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.LEFT;
 import static android.view.Gravity.RIGHT;
@@ -33,16 +37,23 @@
 import static org.junit.Assert.assertEquals;
 
 import android.app.Activity;
+import android.app.ActivityOptions;
 import android.app.Instrumentation;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
 import android.os.Handler;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
-import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowManager;
@@ -51,7 +62,6 @@
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -65,7 +75,6 @@
  */
 // TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
 // TODO: Test non-Activity windows.
-// TODO: Test secondary display.
 @SmallTest
 // TODO(b/68957554)
 //@Presubmit
@@ -78,22 +87,26 @@
     private WindowManager mWm;
     private ArrayList<View> mWindows = new ArrayList<>();
 
-    @Rule
-    public ActivityTestRule<TestActivity> mTestActivityRule = new ActivityTestRule<>(
-            TestActivity.class, false /* initialTouchMode */, false /* launchActivity */);
     private Activity mTestActivity;
+    private VirtualDisplay mDisplay;
+    private ImageReader mImageReader;
 
     private int mDecorThickness;
     private int mHalfDecorThickness;
 
     @Before
     public void setUp() {
-        mWm = mContext.getSystemService(WindowManager.class);
+        final Pair<VirtualDisplay, ImageReader> result = createDisplay();
+        mDisplay = result.first;
+        mImageReader = result.second;
+        final Display display = mDisplay.getDisplay();
+        final Context dContext = mContext.createDisplayContext(display);
+        mWm = dContext.getSystemService(WindowManager.class);
+        mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId());
         final Point size = new Point();
-        mWm.getDefaultDisplay().getSize(size);
+        mDisplay.getDisplay().getRealSize(size);
         mDecorThickness = Math.min(size.x, size.y) / 3;
         mHalfDecorThickness = mDecorThickness / 2;
-        mTestActivity = launchActivity(mTestActivityRule);
     }
 
     @After
@@ -101,7 +114,9 @@
         while (!mWindows.isEmpty()) {
             removeWindow(mWindows.get(0));
         }
-        finishActivity(mTestActivityRule);
+        finishActivity(mTestActivity);
+        mDisplay.release();
+        mImageReader.close();
     }
 
     @Test
@@ -259,23 +274,48 @@
         Assert.assertTrue("Excepted " + first + " >= " + second, first >= second);
     }
 
-    private Activity launchActivity(ActivityTestRule activityRule) {
-        final Activity activity = activityRule.launchActivity(null);
-        waitForIdle();
-        return activity;
-    }
-
-    private void finishActivity(ActivityTestRule activityRule) {
-        final Activity activity = activityRule.getActivity();
-        if (activity != null) {
-            activity.finish();
+    private void finishActivity(Activity a) {
+        if (a == null) {
+            return;
         }
+        a.finish();
+        waitForIdle();
     }
 
     private void waitForIdle() {
         mInstrumentation.waitForIdleSync();
     }
 
+    private Activity startActivityOnDisplay(Class<?> cls, int displayId) {
+        final Intent intent = new Intent(mContext, cls);
+        intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(displayId);
+        final Activity activity = mInstrumentation.startActivitySync(intent, options.toBundle());
+        waitForIdle();
+
+        assertEquals(displayId, activity.getDisplay().getDisplayId());
+        return activity;
+    }
+
+    private Pair<VirtualDisplay, ImageReader> createDisplay() {
+        final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+        final DisplayInfo displayInfo = new DisplayInfo();
+        final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+        defaultDisplay.getDisplayInfo(displayInfo);
+        final String name = "ScreenDecorWindowTests";
+        int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+
+        final ImageReader imageReader = ImageReader.newInstance(
+                displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+        final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth,
+                displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(),
+                flags);
+
+        return Pair.create(display, imageReader);
+    }
+
     public static class TestActivity extends Activity {
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 33d4721..53d0bfb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -17,7 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -133,11 +133,6 @@
     }
 
     @Override
-    public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) {
-
-    }
-
-    @Override
     public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
         return 0;
     }
@@ -189,7 +184,7 @@
 
     @Override
     public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
-        return false;
+        return attrs.type == TYPE_STATUS_BAR;
     }
 
     @Override
@@ -290,40 +285,11 @@
     }
 
     @Override
-    public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
-            int displayRotation, int uiMode) {
-
-    }
-
-    @Override
     public int getSystemDecorLayerLw() {
         return 0;
     }
 
     @Override
-    public void getContentRectLw(Rect r) {
-
-    }
-
-    @Override
-    public void layoutWindowLw(WindowState win,
-            WindowState attached) {
-
-    }
-
-    @Override
-    public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
-            int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
-            Rect outStableInsets, Rect outOutsets) {
-        return false;
-    }
-
-    @Override
-    public void finishLayoutLw() {
-
-    }
-
-    @Override
     public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
 
     }
@@ -582,11 +548,6 @@
     }
 
     @Override
-    public int getInputMethodWindowVisibleHeightLw() {
-        return 0;
-    }
-
-    @Override
     public void setCurrentUserLw(int newUserId) {
 
     }
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index e5d3915..c5ca330 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -103,7 +103,7 @@
         long lastUsedScreenTime;
         @StandbyBuckets int currentBucket;
         String bucketingReason;
-        int lastInformedState;
+        int lastInformedBucket;
     }
 
     AppIdleHistory(File storageDir, long elapsedRealtime) {
@@ -333,13 +333,12 @@
     }
 
     boolean shouldInformListeners(String packageName, int userId,
-            long elapsedRealtime, boolean isIdle) {
+            long elapsedRealtime, int bucket) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                 elapsedRealtime, true);
-        int targetState = isIdle? STATE_IDLE : STATE_ACTIVE;
-        if (appUsageHistory.lastInformedState != (isIdle ? STATE_IDLE : STATE_ACTIVE)) {
-            appUsageHistory.lastInformedState = targetState;
+        if (appUsageHistory.lastInformedBucket != bucket) {
+            appUsageHistory.lastInformedBucket = bucket;
             return true;
         }
         return false;
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 17fde57..5623a68 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -160,7 +160,6 @@
     private final Context mContext;
 
     // TODO: Provide a mechanism to set an external bucketing service
-    private boolean mUseInternalBucketingHeuristics = true;
 
     private AppWidgetManager mAppWidgetManager;
     private PowerManager mPowerManager;
@@ -367,29 +366,33 @@
                     Slog.d(TAG, "   Checking idle state for " + packageName);
                 }
                 if (isSpecial) {
-                    maybeInformListeners(packageName, userId, elapsedRealtime, false);
-                } else if (mUseInternalBucketingHeuristics) {
+                    maybeInformListeners(packageName, userId, elapsedRealtime,
+                            AppStandby.STANDBY_BUCKET_ACTIVE);
+                } else {
                     synchronized (mAppIdleLock) {
-                        int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
-                                elapsedRealtime);
                         String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
                                 userId, elapsedRealtime);
-                        if (bucketingReason != null
-                                && (bucketingReason.equals(AppStandby.REASON_FORCED)
-                                    || bucketingReason.startsWith(AppStandby.REASON_PREDICTED))) {
+                        // If the bucket was forced by the developer, leave it alone
+                        if (AppStandby.REASON_FORCED.equals(bucketingReason)) {
                             continue;
                         }
-                        int newBucket = getBucketForLocked(packageName, userId,
-                                            elapsedRealtime);
-                        if (DEBUG) {
-                            Slog.d(TAG, "     Old bucket=" + oldBucket
-                                    + ", newBucket=" + newBucket);
-                        }
-                        if (oldBucket != newBucket) {
-                            mAppIdleHistory.setAppStandbyBucket(packageName, userId,
-                                    elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
-                            maybeInformListeners(packageName, userId, elapsedRealtime,
-                                    newBucket >= AppStandby.STANDBY_BUCKET_RARE);
+                        // If the bucket was moved up due to usage, let the timeouts apply.
+                        if (AppStandby.REASON_USAGE.equals(bucketingReason)
+                                || AppStandby.REASON_TIMEOUT.equals(bucketingReason)) {
+                            int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
+                                    elapsedRealtime);
+                            int newBucket = getBucketForLocked(packageName, userId,
+                                    elapsedRealtime);
+                            if (DEBUG) {
+                                Slog.d(TAG, "     Old bucket=" + oldBucket
+                                        + ", newBucket=" + newBucket);
+                            }
+                            if (oldBucket < newBucket) {
+                                mAppIdleHistory.setAppStandbyBucket(packageName, userId,
+                                        elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
+                                maybeInformListeners(packageName, userId, elapsedRealtime,
+                                        newBucket);
+                            }
                         }
                     }
                 }
@@ -403,12 +406,12 @@
     }
 
     private void maybeInformListeners(String packageName, int userId,
-            long elapsedRealtime, boolean isIdle) {
+            long elapsedRealtime, int bucket) {
         synchronized (mAppIdleLock) {
             if (mAppIdleHistory.shouldInformListeners(packageName, userId,
-                    elapsedRealtime, isIdle)) {
+                    elapsedRealtime, bucket)) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
-                        userId, isIdle ? 1 : 0, packageName));
+                        userId, bucket, packageName));
             }
         }
     }
@@ -461,11 +464,13 @@
         if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
         boolean paroled = false;
         synchronized (mAppIdleLock) {
-            final long timeSinceLastParole = mInjector.currentTimeMillis() - mLastAppIdleParoledTime;
+            final long timeSinceLastParole =
+                    mInjector.currentTimeMillis() - mLastAppIdleParoledTime;
             if (!deviceIdle
                     && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
                 if (DEBUG) {
-                    Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+                    Slog.i(TAG,
+                            "Bringing idle apps out of inactive state due to deviceIdleMode=false");
                 }
                 paroled = true;
             } else if (deviceIdle) {
@@ -491,7 +496,8 @@
                     || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
                 mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
                 if (previouslyIdle) {
-                    maybeInformListeners(event.mPackage, userId, elapsedRealtime, false);
+                    maybeInformListeners(event.mPackage, userId, elapsedRealtime,
+                            AppStandby.STANDBY_BUCKET_ACTIVE);
                     notifyBatteryStats(event.mPackage, userId, false);
                 }
             }
@@ -729,7 +735,8 @@
 
     void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
             String reason, long elapsedRealtime) {
-        mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason);
+        mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
+                reason);
     }
 
     private boolean isActiveDeviceAdmin(String packageName, int userId) {
@@ -786,9 +793,10 @@
         return packageName != null && packageName.equals(activeScorer);
     }
 
-    void informListeners(String packageName, int userId, boolean isIdle) {
+    void informListeners(String packageName, int userId, int bucket) {
+        final boolean idle = bucket >= AppStandby.STANDBY_BUCKET_RARE;
         for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-            listener.onAppIdleStateChanged(packageName, userId, isIdle);
+            listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
         }
     }
 
@@ -1037,7 +1045,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_INFORM_LISTENERS:
-                    informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
+                    informListeners((String) msg.obj, msg.arg1, msg.arg2);
                     break;
 
                 case MSG_FORCE_IDLE_STATE:
@@ -1187,7 +1195,8 @@
                 mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
                         SCREEN_TIME_THRESHOLDS);
 
-                String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, null);
+                String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
+                        null);
                 mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
                         ELAPSED_TIME_THRESHOLDS);
             }
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 7bea8a1..d359b70 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -255,7 +255,6 @@
     }
 
     private void alsaFileAdded(String name) {
-        Slog.i(TAG, "alsaFileAdded(" + name + ")");
         int type = AlsaDevice.TYPE_UNKNOWN;
         int card = -1, device = -1;
 
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index c29a335..095fdc6 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -19,8 +19,13 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.hardware.usb.UsbConfiguration;
 import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.text.TextUtils;
@@ -32,6 +37,7 @@
 import com.android.server.usb.descriptors.report.TextReportCanvas;
 import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 
@@ -44,23 +50,28 @@
 
     private final Context mContext;
 
+    // contains all connected USB devices
+    private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
+
     // USB busses to exclude from USB host support
     private final String[] mHostBlacklist;
 
+    private final Object mLock = new Object();
+
+    private UsbDevice mNewDevice;
+    private UsbConfiguration mNewConfiguration;
+    private UsbInterface mNewInterface;
+    private ArrayList<UsbConfiguration> mNewConfigurations;
+    private ArrayList<UsbInterface> mNewInterfaces;
+    private ArrayList<UsbEndpoint> mNewEndpoints;
+
     private final UsbAlsaManager mUsbAlsaManager;
     private final UsbSettingsManager mSettingsManager;
 
-    private final Object mLock = new Object();
     @GuardedBy("mLock")
-    // contains all connected USB devices
-    private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
-
-    private Object mSettingsLock = new Object();
-    @GuardedBy("mSettingsLock")
     private UsbProfileGroupSettingsManager mCurrentSettings;
 
-    private Object mHandlerLock = new Object();
-    @GuardedBy("mHandlerLock")
+    @GuardedBy("mLock")
     private ComponentName mUsbDeviceConnectionHandler;
 
     public UsbHostManager(Context context, UsbAlsaManager alsaManager,
@@ -80,33 +91,33 @@
     }
 
     public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
-        synchronized (mSettingsLock) {
+        synchronized (mLock) {
             mCurrentSettings = settings;
         }
     }
 
     private UsbProfileGroupSettingsManager getCurrentUserSettings() {
-        synchronized (mSettingsLock) {
+        synchronized (mLock) {
             return mCurrentSettings;
         }
     }
 
     public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {
-        synchronized (mHandlerLock) {
+        synchronized (mLock) {
             mUsbDeviceConnectionHandler = usbDeviceConnectionHandler;
         }
     }
 
     private @Nullable ComponentName getUsbDeviceConnectionHandler() {
-        synchronized (mHandlerLock) {
+        synchronized (mLock) {
             return mUsbDeviceConnectionHandler;
         }
     }
 
-    private boolean isBlackListed(String deviceAddress) {
+    private boolean isBlackListed(String deviceName) {
         int count = mHostBlacklist.length;
         for (int i = 0; i < count; i++) {
-            if (deviceAddress.startsWith(mHostBlacklist[i])) {
+            if (deviceName.startsWith(mHostBlacklist[i])) {
                 return true;
             }
         }
@@ -125,73 +136,166 @@
     }
 
     /* Called from JNI in monitorUsbHostBus() to report new USB devices
-       Returns true if successful, i.e. the USB Audio device descriptors are
-       correctly parsed and the unique device is added to the audio device list.
+       Returns true if successful, in which case the JNI code will continue adding configurations,
+       interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors
+       have been processed
      */
     @SuppressWarnings("unused")
-    private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
-            byte[] descriptors) {
+    private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID,
+            int deviceClass, int deviceSubclass, int deviceProtocol,
+            String manufacturerName, String productName, int version, String serialNumber) {
+
         if (DEBUG) {
-            Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
+            Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")");
+            // Audio Class Codes:
+            // Audio: 0x01
+            // Audio Subclass Codes:
+            // undefined: 0x00
+            // audio control: 0x01
+            // audio streaming: 0x02
+            // midi streaming: 0x03
+
+            // some useful debugging info
+            Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:"
+                    + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol);
         }
 
-        // check class/subclass first as it is more likely to be blacklisted
-        if (isBlackListed(deviceClass, deviceSubclass) || isBlackListed(deviceAddress)) {
-            if (DEBUG) {
-                Slog.d(TAG, "device is black listed");
-            }
+        // OK this is non-obvious, but true. One can't tell if the device being attached is even
+        // potentially an audio device without parsing the interface descriptors, so punt on any
+        // such test until endUsbDeviceAdded() when we have that info.
+
+        if (isBlackListed(deviceName) ||
+                isBlackListed(deviceClass, deviceSubclass)) {
             return false;
         }
 
         synchronized (mLock) {
-            if (mDevices.get(deviceAddress) != null) {
-                Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
-                //TODO If this is the same peripheral as is being connected, replace
-                // it with the new connection.
+            if (mDevices.get(deviceName) != null) {
+                Slog.w(TAG, "device already on mDevices list: " + deviceName);
                 return false;
             }
 
-            UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress);
-            if (parser.parseDescriptors(descriptors)) {
-
-                UsbDevice newDevice = parser.toAndroidUsbDevice();
-                mDevices.put(deviceAddress, newDevice);
-
-                // It is fine to call this only for the current user as all broadcasts are sent to
-                // all profiles of the user and the dialogs should only show once.
-                ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
-                if (usbDeviceConnectionHandler == null) {
-                    getCurrentUserSettings().deviceAttached(newDevice);
-                } else {
-                    getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
-                            usbDeviceConnectionHandler);
-                }
-
-                // Headset?
-                boolean isInputHeadset = parser.isInputHeadset();
-                boolean isOutputHeadset = parser.isOutputHeadset();
-                Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
-                        + " , out: " + isOutputHeadset + "]");
-
-                mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
-            } else {
-                Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress);
+            if (mNewDevice != null) {
+                Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded");
                 return false;
             }
-        }
 
-        if (DEBUG) {
-            Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
+            // Create version string in "%.%" format
+            String versionString = Integer.toString(version >> 8) + "." + (version & 0xFF);
+
+            mNewDevice = new UsbDevice(deviceName, vendorID, productID,
+                    deviceClass, deviceSubclass, deviceProtocol,
+                    manufacturerName, productName, versionString, serialNumber);
+
+            mNewConfigurations = new ArrayList<>();
+            mNewInterfaces = new ArrayList<>();
+            mNewEndpoints = new ArrayList<>();
         }
 
         return true;
     }
 
+    /* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device
+       currently being added.  Returns true if successful, false in case of error.
+     */
+    @SuppressWarnings("unused")
+    private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
+        if (mNewConfiguration != null) {
+            mNewConfiguration.setInterfaces(
+                    mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
+            mNewInterfaces.clear();
+        }
+
+        mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
+        mNewConfigurations.add(mNewConfiguration);
+    }
+
+    /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device
+       currently being added.  Returns true if successful, false in case of error.
+     */
+    @SuppressWarnings("unused")
+    private void addUsbInterface(int id, String name, int altSetting,
+            int Class, int subClass, int protocol) {
+        if (mNewInterface != null) {
+            mNewInterface.setEndpoints(
+                    mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
+            mNewEndpoints.clear();
+        }
+
+        mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
+        mNewInterfaces.add(mNewInterface);
+    }
+
+    /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device
+       currently being added.  Returns true if successful, false in case of error.
+     */
+    @SuppressWarnings("unused")
+    private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
+        mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
+    }
+
+    /* Called from JNI in monitorUsbHostBus() to finish adding a new device */
+    @SuppressWarnings("unused")
+    private void endUsbDeviceAdded() {
+        if (DEBUG) {
+            Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
+        }
+        if (mNewInterface != null) {
+            mNewInterface.setEndpoints(
+                    mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
+        }
+        if (mNewConfiguration != null) {
+            mNewConfiguration.setInterfaces(
+                    mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
+        }
+
+
+        synchronized (mLock) {
+            if (mNewDevice != null) {
+                mNewDevice.setConfigurations(
+                        mNewConfigurations.toArray(
+                                new UsbConfiguration[mNewConfigurations.size()]));
+                mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
+                Slog.d(TAG, "Added device " + mNewDevice);
+
+                // It is fine to call this only for the current user as all broadcasts are sent to
+                // all profiles of the user and the dialogs should only show once.
+                ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
+                if (usbDeviceConnectionHandler == null) {
+                    getCurrentUserSettings().deviceAttached(mNewDevice);
+                } else {
+                    getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
+                            usbDeviceConnectionHandler);
+                }
+                // deviceName is something like: "/dev/bus/usb/001/001"
+                UsbDescriptorParser parser = new UsbDescriptorParser();
+                boolean isInputHeadset = false;
+                boolean isOutputHeadset = false;
+                if (parser.parseDevice(mNewDevice.getDeviceName())) {
+                    isInputHeadset = parser.isInputHeadset();
+                    isOutputHeadset = parser.isOutputHeadset();
+                    Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
+                            + " , out: " + isOutputHeadset + "]");
+                }
+                mUsbAlsaManager.usbDeviceAdded(mNewDevice,
+                        isInputHeadset, isOutputHeadset);
+            } else {
+                Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
+            }
+            mNewDevice = null;
+            mNewConfigurations = null;
+            mNewInterfaces = null;
+            mNewEndpoints = null;
+            mNewConfiguration = null;
+            mNewInterface = null;
+        }
+    }
+
     /* Called from JNI in monitorUsbHostBus to report USB device removal */
     @SuppressWarnings("unused")
-    private void usbDeviceRemoved(String deviceAddress) {
+    private void usbDeviceRemoved(String deviceName) {
         synchronized (mLock) {
-            UsbDevice device = mDevices.remove(deviceAddress);
+            UsbDevice device = mDevices.remove(deviceName);
             if (device != null) {
                 mUsbAlsaManager.usbDeviceRemoved(device);
                 mSettingsManager.usbDeviceRemoved(device);
@@ -218,37 +322,32 @@
         }
     }
 
-    /**
-     * Opens the specified USB device
-     * @hide
-     */
-    public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings) {
+    /* Opens the specified USB device */
+    public ParcelFileDescriptor openDevice(String deviceName, UsbUserSettingsManager settings) {
         synchronized (mLock) {
-            if (isBlackListed(deviceAddress)) {
+            if (isBlackListed(deviceName)) {
                 throw new SecurityException("USB device is on a restricted bus");
             }
-            UsbDevice device = mDevices.get(deviceAddress);
+            UsbDevice device = mDevices.get(deviceName);
             if (device == null) {
                 // if it is not in mDevices, it either does not exist or is blacklisted
                 throw new IllegalArgumentException(
-                        "device " + deviceAddress + " does not exist or is restricted");
+                        "device " + deviceName + " does not exist or is restricted");
             }
             settings.checkPermission(device);
-            return nativeOpenDevice(deviceAddress);
+            return nativeOpenDevice(deviceName);
         }
     }
 
     public void dump(IndentingPrintWriter pw) {
-        pw.println("USB Host State:");
-        synchronized (mHandlerLock) {
-            if (mUsbDeviceConnectionHandler != null) {
-                pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
-            }
-        }
         synchronized (mLock) {
+            pw.println("USB Host State:");
             for (String name : mDevices.keySet()) {
                 pw.println("  " + name + ": " + mDevices.get(name));
             }
+            if (mUsbDeviceConnectionHandler != null) {
+                pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
+            }
 
             Collection<UsbDevice> devices = mDevices.values();
             if (devices.size() != 0) {
@@ -256,12 +355,17 @@
                 for (UsbDevice device : devices) {
                     StringBuilder stringBuilder = new StringBuilder();
 
-                    UsbDescriptorParser parser = new UsbDescriptorParser(device.getDeviceName());
-                    if (parser.parseDevice()) {
+                    UsbDescriptorParser parser = new UsbDescriptorParser();
+                    if (parser.parseDevice(device.getDeviceName())) {
                         UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
                         descriptorTree.parse(parser);
 
-                        descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
+                        UsbManager usbManager =
+                                (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+                        UsbDeviceConnection connection = usbManager.openDevice(device);
+
+                        descriptorTree.report(new TextReportCanvas(connection, stringBuilder));
+                        connection.close();
 
                         stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
                                 + " , out: " + parser.isOutputHeadset() + "]");
@@ -277,5 +381,5 @@
     }
 
     private native void monitorUsbHostBus();
-    private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
+    private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
 }
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index ebb5a62..917e651 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -32,9 +32,10 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.XmlResourceParser;
+import android.hardware.usb.AccessoryFilter;
+import android.hardware.usb.DeviceFilter;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.os.AsyncTask;
 import android.os.Environment;
@@ -58,7 +59,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -71,7 +71,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 class UsbProfileGroupSettingsManager {
     private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
@@ -157,404 +156,6 @@
         }
     }
 
-    // This class is used to describe a USB device.
-    // When used in HashMaps all values must be specified,
-    // but wildcards can be used for any of the fields in
-    // the package meta-data.
-    private static class DeviceFilter {
-        // USB Vendor ID (or -1 for unspecified)
-        public final int mVendorId;
-        // USB Product ID (or -1 for unspecified)
-        public final int mProductId;
-        // USB device or interface class (or -1 for unspecified)
-        public final int mClass;
-        // USB device subclass (or -1 for unspecified)
-        public final int mSubclass;
-        // USB device protocol (or -1 for unspecified)
-        public final int mProtocol;
-        // USB device manufacturer name string (or null for unspecified)
-        public final String mManufacturerName;
-        // USB device product name string (or null for unspecified)
-        public final String mProductName;
-        // USB device serial number string (or null for unspecified)
-        public final String mSerialNumber;
-
-        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
-                            String manufacturer, String product, String serialnum) {
-            mVendorId = vid;
-            mProductId = pid;
-            mClass = clasz;
-            mSubclass = subclass;
-            mProtocol = protocol;
-            mManufacturerName = manufacturer;
-            mProductName = product;
-            mSerialNumber = serialnum;
-        }
-
-        public DeviceFilter(UsbDevice device) {
-            mVendorId = device.getVendorId();
-            mProductId = device.getProductId();
-            mClass = device.getDeviceClass();
-            mSubclass = device.getDeviceSubclass();
-            mProtocol = device.getDeviceProtocol();
-            mManufacturerName = device.getManufacturerName();
-            mProductName = device.getProductName();
-            mSerialNumber = device.getSerialNumber();
-        }
-
-        public static DeviceFilter read(XmlPullParser parser)
-                throws XmlPullParserException, IOException {
-            int vendorId = -1;
-            int productId = -1;
-            int deviceClass = -1;
-            int deviceSubclass = -1;
-            int deviceProtocol = -1;
-            String manufacturerName = null;
-            String productName = null;
-            String serialNumber = null;
-
-            int count = parser.getAttributeCount();
-            for (int i = 0; i < count; i++) {
-                String name = parser.getAttributeName(i);
-                String value = parser.getAttributeValue(i);
-                // Attribute values are ints or strings
-                if ("manufacturer-name".equals(name)) {
-                    manufacturerName = value;
-                } else if ("product-name".equals(name)) {
-                    productName = value;
-                } else if ("serial-number".equals(name)) {
-                    serialNumber = value;
-                } else {
-                    int intValue;
-                    int radix = 10;
-                    if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
-                        (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
-                        // allow hex values starting with 0x or 0X
-                        radix = 16;
-                        value = value.substring(2);
-                    }
-                    try {
-                        intValue = Integer.parseInt(value, radix);
-                    } catch (NumberFormatException e) {
-                        Slog.e(TAG, "invalid number for field " + name, e);
-                        continue;
-                    }
-                    if ("vendor-id".equals(name)) {
-                        vendorId = intValue;
-                    } else if ("product-id".equals(name)) {
-                        productId = intValue;
-                    } else if ("class".equals(name)) {
-                        deviceClass = intValue;
-                    } else if ("subclass".equals(name)) {
-                        deviceSubclass = intValue;
-                    } else if ("protocol".equals(name)) {
-                        deviceProtocol = intValue;
-                    }
-                }
-            }
-            return new DeviceFilter(vendorId, productId,
-                    deviceClass, deviceSubclass, deviceProtocol,
-                    manufacturerName, productName, serialNumber);
-        }
-
-        public void write(XmlSerializer serializer) throws IOException {
-            serializer.startTag(null, "usb-device");
-            if (mVendorId != -1) {
-                serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
-            }
-            if (mProductId != -1) {
-                serializer.attribute(null, "product-id", Integer.toString(mProductId));
-            }
-            if (mClass != -1) {
-                serializer.attribute(null, "class", Integer.toString(mClass));
-            }
-            if (mSubclass != -1) {
-                serializer.attribute(null, "subclass", Integer.toString(mSubclass));
-            }
-            if (mProtocol != -1) {
-                serializer.attribute(null, "protocol", Integer.toString(mProtocol));
-            }
-            if (mManufacturerName != null) {
-                serializer.attribute(null, "manufacturer-name", mManufacturerName);
-            }
-            if (mProductName != null) {
-                serializer.attribute(null, "product-name", mProductName);
-            }
-            if (mSerialNumber != null) {
-                serializer.attribute(null, "serial-number", mSerialNumber);
-            }
-            serializer.endTag(null, "usb-device");
-        }
-
-        private boolean matches(int clasz, int subclass, int protocol) {
-            return ((mClass == -1 || clasz == mClass) &&
-                    (mSubclass == -1 || subclass == mSubclass) &&
-                    (mProtocol == -1 || protocol == mProtocol));
-        }
-
-        public boolean matches(UsbDevice device) {
-            if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
-            if (mProductId != -1 && device.getProductId() != mProductId) return false;
-            if (mManufacturerName != null && device.getManufacturerName() == null) return false;
-            if (mProductName != null && device.getProductName() == null) return false;
-            if (mSerialNumber != null && device.getSerialNumber() == null) return false;
-            if (mManufacturerName != null && device.getManufacturerName() != null &&
-                !mManufacturerName.equals(device.getManufacturerName())) return false;
-            if (mProductName != null && device.getProductName() != null &&
-                !mProductName.equals(device.getProductName())) return false;
-            if (mSerialNumber != null && device.getSerialNumber() != null &&
-                !mSerialNumber.equals(device.getSerialNumber())) return false;
-
-            // check device class/subclass/protocol
-            if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
-                    device.getDeviceProtocol())) return true;
-
-            // if device doesn't match, check the interfaces
-            int count = device.getInterfaceCount();
-            for (int i = 0; i < count; i++) {
-                UsbInterface intf = device.getInterface(i);
-                 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
-                        intf.getInterfaceProtocol())) return true;
-            }
-
-            return false;
-        }
-
-        /**
-         * If the device described by {@code device} covered by this filter?
-         *
-         * @param device The device
-         *
-         * @return {@code true} iff this filter covers the {@code device}
-         */
-        public boolean contains(DeviceFilter device) {
-            // -1 and null means "match anything"
-
-            if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
-            if (mProductId != -1 && device.mProductId != mProductId) return false;
-            if (mManufacturerName != null && !Objects.equals(mManufacturerName,
-                    device.mManufacturerName)) {
-                return false;
-            }
-            if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
-                return false;
-            }
-            if (mSerialNumber != null
-                    && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
-                return false;
-            }
-
-            // check device class/subclass/protocol
-            return matches(device.mClass, device.mSubclass, device.mProtocol);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            // can't compare if we have wildcard strings
-            if (mVendorId == -1 || mProductId == -1 ||
-                    mClass == -1 || mSubclass == -1 || mProtocol == -1) {
-                return false;
-            }
-            if (obj instanceof DeviceFilter) {
-                DeviceFilter filter = (DeviceFilter)obj;
-
-                if (filter.mVendorId != mVendorId ||
-                        filter.mProductId != mProductId ||
-                        filter.mClass != mClass ||
-                        filter.mSubclass != mSubclass ||
-                        filter.mProtocol != mProtocol) {
-                    return(false);
-                }
-                if ((filter.mManufacturerName != null &&
-                        mManufacturerName == null) ||
-                    (filter.mManufacturerName == null &&
-                        mManufacturerName != null) ||
-                    (filter.mProductName != null &&
-                        mProductName == null)  ||
-                    (filter.mProductName == null &&
-                        mProductName != null) ||
-                    (filter.mSerialNumber != null &&
-                        mSerialNumber == null)  ||
-                    (filter.mSerialNumber == null &&
-                        mSerialNumber != null)) {
-                    return(false);
-                }
-                if  ((filter.mManufacturerName != null &&
-                        mManufacturerName != null &&
-                        !mManufacturerName.equals(filter.mManufacturerName)) ||
-                     (filter.mProductName != null &&
-                        mProductName != null &&
-                        !mProductName.equals(filter.mProductName)) ||
-                     (filter.mSerialNumber != null &&
-                        mSerialNumber != null &&
-                        !mSerialNumber.equals(filter.mSerialNumber))) {
-                    return false;
-                }
-                return true;
-            }
-            if (obj instanceof UsbDevice) {
-                UsbDevice device = (UsbDevice)obj;
-                if (device.getVendorId() != mVendorId ||
-                        device.getProductId() != mProductId ||
-                        device.getDeviceClass() != mClass ||
-                        device.getDeviceSubclass() != mSubclass ||
-                        device.getDeviceProtocol() != mProtocol) {
-                    return(false);
-                }
-                if ((mManufacturerName != null && device.getManufacturerName() == null) ||
-                        (mManufacturerName == null && device.getManufacturerName() != null) ||
-                        (mProductName != null && device.getProductName() == null) ||
-                        (mProductName == null && device.getProductName() != null) ||
-                        (mSerialNumber != null && device.getSerialNumber() == null) ||
-                        (mSerialNumber == null && device.getSerialNumber() != null)) {
-                    return(false);
-                }
-                if ((device.getManufacturerName() != null &&
-                        !mManufacturerName.equals(device.getManufacturerName())) ||
-                        (device.getProductName() != null &&
-                            !mProductName.equals(device.getProductName())) ||
-                        (device.getSerialNumber() != null &&
-                            !mSerialNumber.equals(device.getSerialNumber()))) {
-                    return false;
-                }
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return (((mVendorId << 16) | mProductId) ^
-                    ((mClass << 16) | (mSubclass << 8) | mProtocol));
-        }
-
-        @Override
-        public String toString() {
-            return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
-                    ",mClass=" + mClass + ",mSubclass=" + mSubclass +
-                    ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
-                    ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
-                    "]";
-        }
-    }
-
-    // This class is used to describe a USB accessory.
-    // When used in HashMaps all values must be specified,
-    // but wildcards can be used for any of the fields in
-    // the package meta-data.
-    private static class AccessoryFilter {
-        // USB accessory manufacturer (or null for unspecified)
-        public final String mManufacturer;
-        // USB accessory model (or null for unspecified)
-        public final String mModel;
-        // USB accessory version (or null for unspecified)
-        public final String mVersion;
-
-        public AccessoryFilter(String manufacturer, String model, String version) {
-            mManufacturer = manufacturer;
-            mModel = model;
-            mVersion = version;
-        }
-
-        public AccessoryFilter(UsbAccessory accessory) {
-            mManufacturer = accessory.getManufacturer();
-            mModel = accessory.getModel();
-            mVersion = accessory.getVersion();
-        }
-
-        public static AccessoryFilter read(XmlPullParser parser)
-                throws XmlPullParserException, IOException {
-            String manufacturer = null;
-            String model = null;
-            String version = null;
-
-            int count = parser.getAttributeCount();
-            for (int i = 0; i < count; i++) {
-                String name = parser.getAttributeName(i);
-                String value = parser.getAttributeValue(i);
-
-                if ("manufacturer".equals(name)) {
-                    manufacturer = value;
-                } else if ("model".equals(name)) {
-                    model = value;
-                } else if ("version".equals(name)) {
-                    version = value;
-                }
-             }
-             return new AccessoryFilter(manufacturer, model, version);
-        }
-
-        public void write(XmlSerializer serializer)throws IOException {
-            serializer.startTag(null, "usb-accessory");
-            if (mManufacturer != null) {
-                serializer.attribute(null, "manufacturer", mManufacturer);
-            }
-            if (mModel != null) {
-                serializer.attribute(null, "model", mModel);
-            }
-            if (mVersion != null) {
-                serializer.attribute(null, "version", mVersion);
-            }
-            serializer.endTag(null, "usb-accessory");
-        }
-
-        public boolean matches(UsbAccessory acc) {
-            if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
-            if (mModel != null && !acc.getModel().equals(mModel)) return false;
-            return !(mVersion != null && !acc.getVersion().equals(mVersion));
-        }
-
-        /**
-         * Is the accessories described {@code accessory} covered by this filter?
-         *
-         * @param accessory A filter describing the accessory
-         *
-         * @return {@code true} iff this the filter covers the accessory
-         */
-        public boolean contains(AccessoryFilter accessory) {
-            if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
-                return false;
-            }
-            if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
-            return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion));
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            // can't compare if we have wildcard strings
-            if (mManufacturer == null || mModel == null || mVersion == null) {
-                return false;
-            }
-            if (obj instanceof AccessoryFilter) {
-                AccessoryFilter filter = (AccessoryFilter)obj;
-                return (mManufacturer.equals(filter.mManufacturer) &&
-                        mModel.equals(filter.mModel) &&
-                        mVersion.equals(filter.mVersion));
-            }
-            if (obj instanceof UsbAccessory) {
-                UsbAccessory accessory = (UsbAccessory)obj;
-                return (mManufacturer.equals(accessory.getManufacturer()) &&
-                        mModel.equals(accessory.getModel()) &&
-                        mVersion.equals(accessory.getVersion()));
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
-                    (mModel == null ? 0 : mModel.hashCode()) ^
-                    (mVersion == null ? 0 : mVersion.hashCode()));
-        }
-
-        @Override
-        public String toString() {
-            return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
-                                "\", mModel=\"" + mModel +
-                                "\", mVersion=\"" + mVersion + "\"]";
-        }
-    }
-
     private class MyPackageMonitor extends PackageMonitor {
         @Override
         public void onPackageAdded(String packageName, int uid) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 511e59a..75279c6 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -15,13 +15,8 @@
  */
 package com.android.server.usb.descriptors;
 
-import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbInterface;
-
 import com.android.server.usb.descriptors.report.ReportCanvas;
 
-import java.util.ArrayList;
-
 /**
  * @hide
  * An USB Config Descriptor.
@@ -40,9 +35,6 @@
                                 //     D4..0 Reserved, set to 0.
     private byte mMaxPower;     // 8:1 Maximum Power Consumption in 2mA units
 
-    private ArrayList<UsbInterfaceDescriptor> mInterfaceDescriptors =
-            new ArrayList<UsbInterfaceDescriptor>();
-
     UsbConfigDescriptor(int length, byte type) {
         super(length, type);
         mHierarchyLevel = 2;
@@ -72,22 +64,6 @@
         return mMaxPower;
     }
 
-    void addInterfaceDescriptor(UsbInterfaceDescriptor interfaceDesc) {
-        mInterfaceDescriptors.add(interfaceDesc);
-    }
-
-    UsbConfiguration toAndroid(UsbDescriptorParser parser) {
-        String name = parser.getDescriptorString(mConfigIndex);
-        UsbConfiguration config = new
-                UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
-        UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
-        for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
-            interfaces[index] = mInterfaceDescriptors.get(index).toAndroid(parser);
-        }
-        config.setInterfaces(interfaces);
-        return config;
-    }
-
     @Override
     public int parseRawDescriptors(ByteStream stream) {
         mTotalLength = stream.unpackUsbShort();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index c5052c8..ad7bde5c 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -15,7 +15,6 @@
  */
 package com.android.server.usb.descriptors;
 
-import android.hardware.usb.UsbDevice;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -26,16 +25,11 @@
  */
 public final class UsbDescriptorParser {
     private static final String TAG = "UsbDescriptorParser";
-    private static final boolean DEBUG = false;
-
-    private final String mDeviceAddr;
 
     // Descriptor Objects
-    private static final int DESCRIPTORS_ALLOC_SIZE = 128;
     private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
 
     private UsbDeviceDescriptor mDeviceDescriptor;
-    private UsbConfigDescriptor mCurConfigDescriptor;
     private UsbInterfaceDescriptor mCurInterfaceDescriptor;
 
     // The AudioClass spec implemented by the AudioClass Interfaces
@@ -43,13 +37,7 @@
     // Obtained from the first AudioClass Header descriptor.
     private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
 
-    public UsbDescriptorParser(String deviceAddr) {
-        mDeviceAddr = deviceAddr;
-    }
-
-    public String getDeviceAddr() {
-        return mDeviceAddr;
-    }
+    public UsbDescriptorParser() {}
 
     /**
      * @return the USB Spec value associated with the Device descriptor for the
@@ -72,18 +60,6 @@
     public int getACInterfaceSpec() {
         return mACInterfacesSpec;
     }
-
-    private class UsbDescriptorsStreamFormatException extends Exception {
-        String mMessage;
-        UsbDescriptorsStreamFormatException(String message) {
-            mMessage = message;
-        }
-
-        public String toString() {
-            return "Descriptor Stream Format Exception: " + mMessage;
-        }
-    }
-
     /**
      * The probability (as returned by getHeadsetProbability() at which we conclude
      * the peripheral is a headset.
@@ -91,8 +67,7 @@
     private static final float IN_HEADSET_TRIGGER = 0.75f;
     private static final float OUT_HEADSET_TRIGGER = 0.75f;
 
-    private UsbDescriptor allocDescriptor(ByteStream stream)
-            throws UsbDescriptorsStreamFormatException {
+    private UsbDescriptor allocDescriptor(ByteStream stream) {
         stream.resetReadCount();
 
         int length = stream.getUnsignedByte();
@@ -108,38 +83,15 @@
                 break;
 
             case UsbDescriptor.DESCRIPTORTYPE_CONFIG:
-                descriptor = mCurConfigDescriptor = new UsbConfigDescriptor(length, type);
-                if (mDeviceDescriptor != null) {
-                    mDeviceDescriptor.addConfigDescriptor(mCurConfigDescriptor);
-                } else {
-                    Log.e(TAG, "Config Descriptor found with no associated Device Descriptor!");
-                    throw new UsbDescriptorsStreamFormatException(
-                            "Config Descriptor found with no associated Device Descriptor!");
-                }
+                descriptor = new UsbConfigDescriptor(length, type);
                 break;
 
             case UsbDescriptor.DESCRIPTORTYPE_INTERFACE:
                 descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type);
-                if (mCurConfigDescriptor != null) {
-                    mCurConfigDescriptor.addInterfaceDescriptor(mCurInterfaceDescriptor);
-                } else {
-                    Log.e(TAG, "Interface Descriptor found with no associated Config Descriptor!");
-                    throw new UsbDescriptorsStreamFormatException(
-                            "Interface Descriptor found with no associated Config Descriptor!");
-                }
                 break;
 
             case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT:
                 descriptor = new UsbEndpointDescriptor(length, type);
-                if (mCurInterfaceDescriptor != null) {
-                    mCurInterfaceDescriptor.addEndpointDescriptor(
-                            (UsbEndpointDescriptor) descriptor);
-                } else {
-                    Log.e(TAG,
-                            "Endpoint Descriptor found with no associated Interface Descriptor!");
-                    throw new UsbDescriptorsStreamFormatException(
-                            "Endpoint Descriptor found with no associated Interface Descriptor!");
-                }
                 break;
 
             /*
@@ -192,12 +144,8 @@
     /**
      * @hide
      */
-    public boolean parseDescriptors(byte[] descriptors) {
-        if (DEBUG) {
-            Log.d(TAG, "parseDescriptors() - start");
-        }
-        // This will allow us to (probably) alloc mDescriptors just once.
-        mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
+    public void parseDescriptors(byte[] descriptors) {
+        mDescriptors.clear();
 
         ByteStream stream = new ByteStream(descriptors);
         while (stream.available() > 0) {
@@ -225,36 +173,21 @@
                 }
             }
         }
-        if (DEBUG) {
-            Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors.");
+    }
+
+    /**
+     * @hide
+     */
+    public boolean parseDevice(String deviceAddr) {
+        byte[] rawDescriptors = getRawDescriptors(deviceAddr);
+        if (rawDescriptors != null) {
+            parseDescriptors(rawDescriptors);
+            return true;
         }
-        return true;
+        return false;
     }
 
-    /**
-     * @hide
-     */
-    public boolean parseDevice() {
-        byte[] rawDescriptors = getRawDescriptors();
-
-        return rawDescriptors != null
-            ? parseDescriptors(rawDescriptors) : false;
-    }
-
-    private byte[] getRawDescriptors() {
-        return getRawDescriptors_native(mDeviceAddr);
-    }
-
-    private native byte[] getRawDescriptors_native(String deviceAddr);
-
-    /**
-     * @hide
-     */
-    public String getDescriptorString(int stringId) {
-        return getDescriptorString_native(mDeviceAddr, stringId);
-    }
-
-    private native String getDescriptorString_native(String deviceAddr, int stringId);
+    private native byte[] getRawDescriptors(String deviceAddr);
 
     public int getParsingSpec() {
         return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0;
@@ -267,17 +200,6 @@
     /**
      * @hide
      */
-    public UsbDevice toAndroidUsbDevice() {
-        if (mDeviceDescriptor == null) {
-            return null;
-        }
-
-        return mDeviceDescriptor.toAndroid(this);
-    }
-
-    /**
-     * @hide
-     */
     public ArrayList<UsbDescriptor> getDescriptors(byte type) {
         ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
         for (UsbDescriptor descriptor : mDescriptors) {
@@ -433,6 +355,8 @@
      * to count on the peripheral being a headset.
      */
     public boolean isInputHeadset() {
+        // TEMP
+        Log.i(TAG, "---- isInputHeadset() prob:" + (getInputHeadsetProbability() * 100f) + "%");
         return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
     }
 
@@ -486,6 +410,8 @@
      * to count on the peripheral being a headset.
      */
     public boolean isOutputHeadset() {
+        // TEMP
+        Log.i(TAG, "---- isOutputHeadset() prob:" + (getOutputHeadsetProbability() * 100f) + "%");
         return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
     }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index c89d9b6..d5cb89e 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -15,14 +15,9 @@
  */
 package com.android.server.usb.descriptors;
 
-import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbDevice;
-
 import com.android.server.usb.descriptors.report.ReportCanvas;
 import com.android.server.usb.descriptors.report.UsbStrings;
 
-import java.util.ArrayList;
-
 /**
  * @hide
  * A USB Device Descriptor.
@@ -49,9 +44,6 @@
     private byte mSerialNum;    // 16:1 Index of Serial Number String Descriptor
     private byte mNumConfigs;   // 17:1 Number of Possible Configurations
 
-    private ArrayList<UsbConfigDescriptor> mConfigDescriptors =
-            new ArrayList<UsbConfigDescriptor>();
-
     UsbDeviceDescriptor(int length, byte type) {
         super(length, type);
         mHierarchyLevel = 1;
@@ -105,35 +97,6 @@
         return mNumConfigs;
     }
 
-    void addConfigDescriptor(UsbConfigDescriptor config) {
-        mConfigDescriptors.add(config);
-    }
-
-    /**
-     * @hide
-     */
-    public UsbDevice toAndroid(UsbDescriptorParser parser) {
-        String mfgName = parser.getDescriptorString(mMfgIndex);
-        String prodName = parser.getDescriptorString(mProductIndex);
-
-        // Create version string in "%.%" format
-        String versionString =
-                Integer.toString(mDeviceRelease >> 8) + "." + (mDeviceRelease & 0xFF);
-        String serialStr = parser.getDescriptorString(mSerialNum);
-
-        UsbDevice device = new UsbDevice(parser.getDeviceAddr(), mVendorID, mProductID,
-                mDevClass, mDevSubClass,
-                mProtocol, mfgName, prodName,
-                versionString, serialStr);
-        UsbConfiguration[] configs = new UsbConfiguration[mConfigDescriptors.size()];
-        for (int index = 0; index < mConfigDescriptors.size(); index++) {
-            configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
-        }
-        device.setConfigurations(configs);
-
-        return device;
-    }
-
     @Override
     public int parseRawDescriptors(ByteStream stream) {
         mSpec = stream.unpackUsbShort();
@@ -171,11 +134,12 @@
                 + " Product ID: " + ReportCanvas.getHexString(getProductID())
                 + " Product Release: " + ReportCanvas.getBCDString(getDeviceRelease()));
 
-        UsbDescriptorParser parser = canvas.getParser();
         byte mfgIndex = getMfgIndex();
-        String manufacturer = parser.getDescriptorString(mfgIndex);
+        String manufacturer =
+                UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), mfgIndex);
         byte productIndex = getProductIndex();
-        String product = parser.getDescriptorString(productIndex);
+        String product =
+                UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), productIndex);
 
         canvas.writeListItem("Manufacturer " + mfgIndex + ": " + manufacturer
                 + " Product " + productIndex + ": " + product);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 9cb8f05..6322fbe 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -15,8 +15,6 @@
  */
 package com.android.server.usb.descriptors;
 
-import android.hardware.usb.UsbEndpoint;
-
 import com.android.server.usb.descriptors.report.ReportCanvas;
 
 /**
@@ -107,10 +105,6 @@
         return mSyncAddress;
     }
 
-    /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
-        return new UsbEndpoint(mEndpointAddress, mAttributes, mPacketSize, mInterval);
-    }
-
     @Override
     public int parseRawDescriptors(ByteStream stream) {
         mEndpointAddress = stream.getByte();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 645807d..4eef6ca 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -15,14 +15,9 @@
  */
 package com.android.server.usb.descriptors;
 
-import android.hardware.usb.UsbEndpoint;
-import android.hardware.usb.UsbInterface;
-
 import com.android.server.usb.descriptors.report.ReportCanvas;
 import com.android.server.usb.descriptors.report.UsbStrings;
 
-import java.util.ArrayList;
-
 /**
  * @hide
  * A common super-class for all USB Interface Descritor subtypes.
@@ -39,9 +34,6 @@
     protected byte mProtocol;         // 7:1 Protocol Code
     protected byte mDescrIndex;       // 8:1 Index of String Descriptor Describing this interface
 
-    private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
-            new ArrayList<UsbEndpointDescriptor>();
-
     UsbInterfaceDescriptor(int length, byte type) {
         super(length, type);
         mHierarchyLevel = 3;
@@ -88,22 +80,6 @@
         return mDescrIndex;
     }
 
-    void addEndpointDescriptor(UsbEndpointDescriptor endpoint) {
-        mEndpointDescriptors.add(endpoint);
-    }
-
-    UsbInterface toAndroid(UsbDescriptorParser parser) {
-        String name = parser.getDescriptorString(mDescrIndex);
-        UsbInterface ntrface = new UsbInterface(
-                mInterfaceNumber, mAlternateSetting, name, mUsbClass, mUsbSubclass, mProtocol);
-        UsbEndpoint[] endpoints = new UsbEndpoint[mEndpointDescriptors.size()];
-        for (int index = 0; index < mEndpointDescriptors.size(); index++) {
-            endpoints[index] = mEndpointDescriptors.get(index).toAndroid(parser);
-        }
-        ntrface.setEndpoints(endpoints);
-        return ntrface;
-    }
-
     @Override
     public void report(ReportCanvas canvas) {
         super.report(canvas);
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
index adfc514..99ebcca 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
@@ -15,7 +15,7 @@
  */
 package com.android.server.usb.descriptors.report;
 
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
 
 /**
  * @hide
@@ -32,8 +32,8 @@
      * from the USB device.
      * @param stringBuilder Generated output gets written into this object.
      */
-    public HTMLReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) {
-        super(parser);
+    public HTMLReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
+        super(connection);
 
         mStringBuilder = stringBuilder;
     }
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
index c34dc98..9e0adf5 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
@@ -15,7 +15,7 @@
  */
 package com.android.server.usb.descriptors.report;
 
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
 
 /**
  * @hide
@@ -24,19 +24,22 @@
 public abstract class ReportCanvas {
     private static final String TAG = "ReportCanvas";
 
-    private final UsbDescriptorParser mParser;
+    private final UsbDeviceConnection mConnection;
 
     /**
      * Constructor.
      * @param connection    The USB connection object used to retrieve strings
      * from the USB device.
      */
-    public ReportCanvas(UsbDescriptorParser parser) {
-        mParser = parser;
+    public ReportCanvas(UsbDeviceConnection connection) {
+        mConnection = connection;
     }
 
-    public UsbDescriptorParser getParser() {
-        return mParser;
+    /**
+     * @returns the UsbDeviceConnection member (mConnection).
+     */
+    public UsbDeviceConnection getConnection() {
+        return mConnection;
     }
 
     /**
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
index 1e19ea1..a43569d 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
@@ -15,7 +15,7 @@
  */
 package com.android.server.usb.descriptors.report;
 
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
 
 /**
  * @hide
@@ -34,8 +34,8 @@
      * from the USB device.
      * @param stringBuilder Generated output gets written into this object.
      */
-    public TextReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) {
-        super(parser);
+    public TextReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
+        super(connection);
 
         mStringBuilder = stringBuilder;
     }
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index f601d8b..4b827d2 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -16,16 +16,35 @@
 
 package android.telecom;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothDevice;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  *  Encapsulates the telecom audio state, including the current audio routing, supported audio
  *  routing and mute.
  */
 public final class CallAudioState implements Parcelable {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
+            flag=true)
+    public @interface CallAudioRoute {}
+
     /** Direct the audio stream through the device's earpiece. */
     public static final int ROUTE_EARPIECE      = 0x00000001;
 
@@ -55,6 +74,8 @@
     private final boolean isMuted;
     private final int route;
     private final int supportedRouteMask;
+    private final BluetoothDevice activeBluetoothDevice;
+    private final Collection<BluetoothDevice> supportedBluetoothDevices;
 
     /**
      * Constructor for a {@link CallAudioState} object.
@@ -73,10 +94,21 @@
      * {@link #ROUTE_WIRED_HEADSET}
      * {@link #ROUTE_SPEAKER}
      */
-    public CallAudioState(boolean muted, int route, int supportedRouteMask) {
-        this.isMuted = muted;
+    public CallAudioState(boolean muted, @CallAudioRoute int route,
+            @CallAudioRoute int supportedRouteMask) {
+        this(muted, route, supportedRouteMask, null, Collections.emptyList());
+    }
+
+    /** @hide */
+    public CallAudioState(boolean isMuted, @CallAudioRoute int route,
+            @CallAudioRoute int supportedRouteMask,
+            @Nullable BluetoothDevice activeBluetoothDevice,
+            @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) {
+        this.isMuted = isMuted;
         this.route = route;
         this.supportedRouteMask = supportedRouteMask;
+        this.activeBluetoothDevice = activeBluetoothDevice;
+        this.supportedBluetoothDevices = supportedBluetoothDevices;
     }
 
     /** @hide */
@@ -84,6 +116,8 @@
         isMuted = state.isMuted();
         route = state.getRoute();
         supportedRouteMask = state.getSupportedRouteMask();
+        activeBluetoothDevice = state.activeBluetoothDevice;
+        supportedBluetoothDevices = state.getSupportedBluetoothDevices();
     }
 
     /** @hide */
@@ -92,6 +126,8 @@
         isMuted = state.isMuted();
         route = state.getRoute();
         supportedRouteMask = state.getSupportedRouteMask();
+        activeBluetoothDevice = null;
+        supportedBluetoothDevices = Collections.emptyList();
     }
 
     @Override
@@ -103,17 +139,32 @@
             return false;
         }
         CallAudioState state = (CallAudioState) obj;
-        return isMuted() == state.isMuted() && getRoute() == state.getRoute() &&
-                getSupportedRouteMask() == state.getSupportedRouteMask();
+        if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) {
+            return false;
+        }
+        for (BluetoothDevice device : supportedBluetoothDevices) {
+            if (!state.supportedBluetoothDevices.contains(device)) {
+                return false;
+            }
+        }
+        return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() ==
+                state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() ==
+                state.getSupportedRouteMask();
     }
 
     @Override
     public String toString() {
+        String bluetoothDeviceList = supportedBluetoothDevices.stream()
+                .map(BluetoothDevice::getAddress).collect(Collectors.joining(", "));
+
         return String.format(Locale.US,
-                "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s]",
+                "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " +
+                        "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]",
                 isMuted,
                 audioRouteToString(route),
-                audioRouteToString(supportedRouteMask));
+                audioRouteToString(supportedRouteMask),
+                activeBluetoothDevice,
+                bluetoothDeviceList);
     }
 
     /**
@@ -126,6 +177,7 @@
     /**
      * @return The current audio route being used.
      */
+    @CallAudioRoute
     public int getRoute() {
         return route;
     }
@@ -133,11 +185,27 @@
     /**
      * @return Bit mask of all routes supported by this call.
      */
+    @CallAudioRoute
     public int getSupportedRouteMask() {
         return supportedRouteMask;
     }
 
     /**
+     * @return The {@link BluetoothDevice} through which audio is being routed.
+     *         Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
+     */
+    public BluetoothDevice getActiveBluetoothDevice() {
+        return activeBluetoothDevice;
+    }
+
+    /**
+     * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
+     */
+    public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
+        return supportedBluetoothDevices;
+    }
+
+    /**
      * Converts the provided audio route into a human readable string representation.
      *
      * @param route to convert into a string.
@@ -177,7 +245,13 @@
             boolean isMuted = source.readByte() == 0 ? false : true;
             int route = source.readInt();
             int supportedRouteMask = source.readInt();
-            return new CallAudioState(isMuted, route, supportedRouteMask);
+            BluetoothDevice activeBluetoothDevice = source.readParcelable(
+                    ClassLoader.getSystemClassLoader());
+            List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
+            source.readParcelableList(supportedBluetoothDevices,
+                    ClassLoader.getSystemClassLoader());
+            return new CallAudioState(isMuted, route,
+                    supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
         }
 
         @Override
@@ -202,6 +276,8 @@
         destination.writeByte((byte) (isMuted ? 1 : 0));
         destination.writeInt(route);
         destination.writeInt(supportedRouteMask);
+        destination.writeParcelable(activeBluetoothDevice, 0);
+        destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
     }
 
     private static void listAppend(StringBuffer buffer, String str) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8ba934c..ffb5e93 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.Notification;
+import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
 import android.hardware.camera2.CameraManager;
 import android.net.Uri;
@@ -819,7 +820,7 @@
         public void onConnectionEvent(Connection c, String event, Bundle extras) {}
         /** @hide */
         public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
-        public void onAudioRouteChanged(Connection c, int audioRoute) {}
+        public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {}
         public void onRttInitiationSuccess(Connection c) {}
         public void onRttInitiationFailure(Connection c, int reason) {}
         public void onRttSessionRemotelyTerminated(Connection c) {}
@@ -2576,7 +2577,29 @@
      */
     public final void setAudioRoute(int route) {
         for (Listener l : mListeners) {
-            l.onAudioRouteChanged(this, route);
+            l.onAudioRouteChanged(this, route, null);
+        }
+    }
+
+    /**
+     *
+     * Request audio routing to a specific bluetooth device. Calling this method may result in
+     * the device routing audio to a different bluetooth device than the one specified if the
+     * bluetooth stack is unable to route audio to the requested device.
+     * A list of available devices can be obtained via
+     * {@link CallAudioState#getSupportedBluetoothDevices()}
+     *
+     * <p>
+     * Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a
+     * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
+     * <p>
+     * See also {@link InCallService#requestBluetoothAudio(String)}
+     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+     *                         {@link BluetoothDevice#getAddress()}.
+     */
+    public void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+        for (Listener l : mListeners) {
+            l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
         }
     }
 
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index a81fba9..35804f6 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1294,10 +1294,10 @@
         }
 
         @Override
-        public void onAudioRouteChanged(Connection c, int audioRoute) {
+        public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {
             String id = mIdByConnection.get(c);
             if (id != null) {
-                mAdapter.setAudioRoute(id, audioRoute);
+                mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress);
             }
         }
 
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 111fcc7..92a9dc2 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -520,11 +520,14 @@
      * @param callId The unique ID of the call.
      * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
      */
-    void setAudioRoute(String callId, int audioRoute) {
-        Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute));
+    void setAudioRoute(String callId, int audioRoute, String bluetoothAddress) {
+        Log.v(this, "setAudioRoute: %s %s %s", callId,
+                CallAudioState.audioRouteToString(audioRoute),
+                bluetoothAddress);
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession());
+                adapter.setAudioRoute(callId, audioRoute,
+                        bluetoothAddress, Log.getExternalSession());
             } catch (RemoteException ignored) {
             }
         }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index b1617f4..3fbdeb1 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -298,8 +298,8 @@
                 case MSG_SET_AUDIO_ROUTE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.setAudioRoute((String) args.arg1, args.argi1,
-                                (Session.Info) args.arg2);
+                        mDelegate.setAudioRoute((String) args.arg1, args.argi1, (String) args.arg2,
+                                (Session.Info) args.arg3);
                     } finally {
                         args.recycle();
                     }
@@ -548,12 +548,12 @@
 
         @Override
         public final void setAudioRoute(String connectionId, int audioRoute,
-                Session.Info sessionInfo) {
-
+                String bluetoothAddress, Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = connectionId;
             args.argi1 = audioRoute;
-            args.arg2 = sessionInfo;
+            args.arg2 = bluetoothAddress;
+            args.arg3 = sessionInfo;
             mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, args).sendToTarget();
         }
 
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 9559a28..9bf0467 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.bluetooth.BluetoothDevice;
 import android.os.Bundle;
 import android.os.RemoteException;
 
@@ -128,7 +129,22 @@
      */
     public void setAudioRoute(int route) {
         try {
-            mAdapter.setAudioRoute(route);
+            mAdapter.setAudioRoute(route, null);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Request audio routing to a specific bluetooth device. Calling this method may result in
+     * the device routing audio to a different bluetooth device than the one specified. A list of
+     * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
+     *
+     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+     * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
+     */
+    public void requestBluetoothAudio(String bluetoothAddress) {
+        try {
+            mAdapter.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
         } catch (RemoteException e) {
         }
     }
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index e384d46..d558bba 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -16,9 +16,11 @@
 
 package android.telecom;
 
+import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
 import android.hardware.camera2.CameraManager;
 import android.net.Uri;
@@ -377,6 +379,22 @@
     }
 
     /**
+     * Request audio routing to a specific bluetooth device. Calling this method may result in
+     * the device routing audio to a different bluetooth device than the one specified if the
+     * bluetooth stack is unable to route audio to the requested device.
+     * A list of available devices can be obtained via
+     * {@link CallAudioState#getSupportedBluetoothDevices()}
+     *
+     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+     *                         {@link BluetoothDevice#getAddress()}.
+     */
+    public final void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+        if (mPhone != null) {
+            mPhone.requestBluetoothAudio(bluetoothAddress);
+        }
+    }
+
+    /**
      * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
      * to start displaying in-call information to the user. Each instance of {@code InCallService}
      * will have only one {@code Phone}, and this method will be called exactly once in the lifetime
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 066f6c2..421b1a4 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -17,7 +17,9 @@
 package android.telecom;
 
 import android.annotation.SystemApi;
+import android.bluetooth.BluetoothDevice;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.util.ArrayMap;
 
 import java.util.Collections;
@@ -295,6 +297,18 @@
     }
 
     /**
+     * Request audio routing to a specific bluetooth device. Calling this method may result in
+     * the device routing audio to a different bluetooth device than the one specified. A list of
+     * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
+     *
+     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+     * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
+     */
+    public void requestBluetoothAudio(String bluetoothAddress) {
+        mInCallAdapter.requestBluetoothAudio(bluetoothAddress);
+    }
+
+    /**
      * Turns the proximity sensor on. When this request is made, the proximity sensor will
      * become active, and the touch screen and display will be turned off when the user's face
      * is detected to be in close proximity to the screen. This operation is a no-op on devices
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 2cc4314..85906ad 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -398,7 +398,8 @@
         }
 
         @Override
-        public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) {
+        public void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
+                Session.Info sessionInfo) {
             if (hasConnection(callId)) {
                 // TODO(3pcalls): handle this for remote connections.
                 // Likely we don't want to do anything since it doesn't make sense for self-managed
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index d20da18..da2015f 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -103,7 +103,8 @@
 
     void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
 
-    void setAudioRoute(String callId, int audioRoute, in Session.Info sessionInfo);
+    void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
+            in Session.Info sessionInfo);
 
     void onConnectionEvent(String callId, String event, in Bundle extras,
     in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 73fa29a..bce3392 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -39,7 +39,7 @@
 
     void mute(boolean shouldMute);
 
-    void setAudioRoute(int route);
+    void setAudioRoute(int route, String bluetoothAddress);
 
     void playDtmfTone(String callId, char digit);
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 061e55a..0030ab6 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1650,6 +1650,26 @@
     public static final String KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL =
             "identify_high_definition_calls_in_call_log_bool";
 
+    /**
+     * Flag specifying whether to use the {@link ServiceState} roaming status, which can be
+     * affected by other carrier configs (e.g.
+     * {@link #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY}), when setting the SPN display.
+     * <p>
+     * If {@code true}, the SPN display uses {@link ServiceState#getRoaming}.
+     * If {@code false} the SPN display checks if the current MCC/MNC is different from the
+     * SIM card's MCC/MNC.
+     *
+     * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see KEY_NON_ROAMING_OPERATOR_STRING_ARRAY
+     * @see KEY_ROAMING_OPERATOR_STRING_ARRAY
+     * @see KEY_FORCE_HOME_NETWORK_BOOL
+     *
+     * @hide
+     */
+    public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL =
+            "spn_display_rule_use_roaming_from_service_state_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1928,6 +1948,7 @@
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
         sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
+        sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index afff6d5..9ccfa94 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -204,16 +204,6 @@
     public static final int LISTEN_VOLTE_STATE                              = 0x00004000;
 
     /**
-     * Listen for OEM hook raw event
-     *
-     * @see #onOemHookRawEvent
-     * @hide
-     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
-     */
-    @Deprecated
-    public static final int LISTEN_OEM_HOOK_RAW_EVENT                       = 0x00008000;
-
-    /**
      * Listen for carrier network changes indicated by a carrier app.
      *
      * @see #onCarrierNetworkRequest
@@ -359,9 +349,6 @@
                     case LISTEN_DATA_ACTIVATION_STATE:
                         PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
                         break;
-                    case LISTEN_OEM_HOOK_RAW_EVENT:
-                        PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
-                        break;
                     case LISTEN_CARRIER_NETWORK_CHANGE:
                         PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
                         break;
@@ -556,16 +543,6 @@
     }
 
     /**
-     * Callback invoked when OEM hook raw event is received. Requires
-     * the READ_PRIVILEGED_PHONE_STATE permission.
-     * @param rawData is the byte array of the OEM hook raw data.
-     * @hide
-     */
-    public void onOemHookRawEvent(byte[] rawData) {
-        // default implementation empty
-    }
-
-    /**
      * Callback invoked when telephony has received notice from a carrier
      * app that a network action that could result in connectivity loss
      * has been requested by an app using
@@ -677,10 +654,6 @@
             send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
         }
 
-        public void onOemHookRawEvent(byte[] rawData) {
-            send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
-        }
-
         public void onCarrierNetworkChange(boolean active) {
             send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
         }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 88f4880..2f39ddb 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -360,6 +360,42 @@
     public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
 
     /**
+     * TelephonyProvider column name for enable Volte.
+     *@hide
+     */
+    public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+
+    /**
+     * TelephonyProvider column name for enable VT (Video Telephony over IMS)
+     *@hide
+     */
+    public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+
+    /**
+     * TelephonyProvider column name for enable Wifi calling
+     *@hide
+     */
+    public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+
+    /**
+     * TelephonyProvider column name for Wifi calling mode
+     *@hide
+     */
+    public static final String WFC_IMS_MODE = "wfc_ims_mode";
+
+    /**
+     * TelephonyProvider column name for Wifi calling mode in roaming
+     *@hide
+     */
+    public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+
+    /**
+     * TelephonyProvider column name for enable Wifi calling in roaming
+     *@hide
+     */
+    public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+
+    /**
      * Broadcast Action: The user has changed one of the default subs related to
      * data, phone calls, or sms</p>
      *
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index e40f20f..d7b6142 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -2828,6 +2828,26 @@
          *  @hide
          */
         public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
+
+        /**
+         * The owner of the APN.
+         * <p>Type: INTEGER</p>
+         * @hide
+         */
+        public static final String OWNED_BY = "owned_by";
+
+        /**
+         * Possible value for the OWNED_BY field.
+         * APN is owned by DPC.
+         * @hide
+         */
+        public static final int OWNED_BY_DPC = 0;
+        /**
+         * Possible value for the OWNED_BY field.
+         * APN is owned by other sources.
+         * @hide
+         */
+        public static final int OWNED_BY_OTHERS = 1;
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 42c3de5..4ffb3c3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5709,29 +5709,6 @@
         return retVal;
     }
 
-    /**
-     * Returns the result and response from RIL for oem request
-     *
-     * @param oemReq the data is sent to ril.
-     * @param oemResp the respose data from RIL.
-     * @return negative value request was not handled or get error
-     *         0 request was handled succesfully, but no response data
-     *         positive value success, data length of response
-     * @hide
-     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
-     */
-    @Deprecated
-    public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
-        } catch (RemoteException ex) {
-        } catch (NullPointerException ex) {
-        }
-        return -1;
-    }
-
     /** @hide */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index b5484e34..01041c8 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -53,6 +53,8 @@
     @Nullable
     public final String encodedActivationCode;
 
+    @Nullable private String confirmationCode;
+
     // see getCarrierName and setCarrierName
     @Nullable
     private String carrierName;
@@ -66,6 +68,7 @@
 
     private DownloadableSubscription(Parcel in) {
         encodedActivationCode = in.readString();
+        confirmationCode = in.readString();
         carrierName = in.readString();
         accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
     }
@@ -83,6 +86,21 @@
     }
 
     /**
+     * Sets the confirmation code.
+     */
+    public void setConfirmationCode(String confirmationCode) {
+        this.confirmationCode = confirmationCode;
+    }
+
+    /**
+     * Returns the confirmation code.
+     */
+    @Nullable
+    public String getConfirmationCode() {
+        return confirmationCode;
+    }
+
+    /**
      * Set the user-visible carrier name.
      * @hide
      *
@@ -134,6 +152,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(encodedActivationCode);
+        dest.writeString(confirmationCode);
         dest.writeString(carrierName);
         dest.writeTypedArray(accessRules, flags);
     }
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
deleted file mode 100644
index 31d3db4..0000000
--- a/telephony/java/android/telephony/ims/ImsServiceProxy.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.feature.IRcsFeature;
-import android.telephony.ims.feature.ImsFeature;
-import android.util.Log;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
- * the platform currently supports: MMTel and RCS.
- * @hide
- */
-
-public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature {
-
-    protected String LOG_TAG = "ImsServiceProxy";
-    private final int mSupportedFeature;
-
-    // Start by assuming the proxy is available for usage.
-    private boolean mIsAvailable = true;
-    // ImsFeature Status from the ImsService. Cached.
-    private Integer mFeatureStatusCached = null;
-    private ImsServiceProxy.INotifyStatusChanged mStatusCallback;
-    private final Object mLock = new Object();
-
-    public interface INotifyStatusChanged {
-        void notifyStatusChanged();
-    }
-
-    private final IImsServiceFeatureListener mListenerBinder =
-            new IImsServiceFeatureListener.Stub() {
-
-        @Override
-        public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
-            // The feature has been re-enabled. This may happen when the service crashes.
-            synchronized (mLock) {
-                if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
-                    Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
-                            feature);
-                    mIsAvailable = true;
-                }
-            }
-        }
-
-        @Override
-        public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
-            synchronized (mLock) {
-                if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
-                    Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
-                            feature);
-                    mIsAvailable = false;
-                }
-            }
-        }
-
-        @Override
-        public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
-            synchronized (mLock) {
-                Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
-                        " status: " + status);
-                if (mSlotId == slotId && feature == mSupportedFeature) {
-                    mFeatureStatusCached = status;
-                    if (mStatusCallback != null) {
-                        mStatusCallback.notifyStatusChanged();
-                    }
-                }
-            }
-        }
-    };
-
-    public ImsServiceProxy(int slotId, IBinder binder, int featureType) {
-        super(slotId, binder);
-        mSupportedFeature = featureType;
-    }
-
-    public ImsServiceProxy(int slotId, int featureType) {
-        super(slotId, null /*IBinder*/);
-        mSupportedFeature = featureType;
-    }
-
-    public IImsServiceFeatureListener getListener() {
-        return mListenerBinder;
-    }
-
-    public void setBinder(IBinder binder) {
-        mBinder = binder;
-    }
-
-    @Override
-    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
-                    incomingCallIntent, listener);
-        }
-    }
-
-    @Override
-    public void endSession(int sessionId) throws RemoteException {
-        synchronized (mLock) {
-            // Only check to make sure the binder connection still exists. This method should
-            // still be able to be called when the state is STATE_NOT_AVAILABLE.
-            checkBinderConnection();
-            getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
-        }
-    }
-
-    @Override
-    public boolean isConnected(int callServiceType, int callType)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
-                    callServiceType, callType);
-        }
-    }
-
-    @Override
-    public boolean isOpened() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
-        }
-    }
-
-    @Override
-    public void addRegistrationListener(IImsRegistrationListener listener)
-    throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
-                    listener);
-        }
-    }
-
-    @Override
-    public void removeRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
-                    listener);
-        }
-    }
-
-    @Override
-    public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
-                    sessionId, callServiceType, callType);
-        }
-    }
-
-    @Override
-    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
-            IImsCallSessionListener listener) throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
-                    sessionId, profile, listener);
-        }
-    }
-
-    @Override
-    public IImsCallSession getPendingCallSession(int sessionId, String callId)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
-                    sessionId, callId);
-        }
-    }
-
-    @Override
-    public IImsUt getUtInterface() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
-        }
-    }
-
-    @Override
-    public IImsConfig getConfigInterface() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
-        }
-    }
-
-    @Override
-    public void turnOnIms() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
-        }
-    }
-
-    @Override
-    public void turnOffIms() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
-        }
-    }
-
-    @Override
-    public IImsEcbm getEcbmInterface() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
-        }
-    }
-
-    @Override
-    public void setUiTTYMode(int uiTtyMode, Message onComplete)
-            throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
-                    onComplete);
-        }
-    }
-
-    @Override
-    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-        synchronized (mLock) {
-            checkServiceIsReady();
-            return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
-                    mSupportedFeature);
-        }
-    }
-
-    @Override
-    public int getFeatureStatus() {
-        synchronized (mLock) {
-            if (isBinderAlive() && mFeatureStatusCached != null) {
-                Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
-                return mFeatureStatusCached;
-            }
-        }
-        // Don't synchronize on Binder call.
-        Integer status = retrieveFeatureStatus();
-        synchronized (mLock) {
-            if (status == null) {
-                return ImsFeature.STATE_NOT_AVAILABLE;
-            }
-            // Cache only non-null value for feature status.
-            mFeatureStatusCached = status;
-        }
-        Log.i(LOG_TAG, "getFeatureStatus - returning " + status);
-        return status;
-    }
-
-    /**
-     * Internal method used to retrieve the feature status from the corresponding ImsService.
-     */
-    private Integer retrieveFeatureStatus() {
-        if (mBinder != null) {
-            try {
-                return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature);
-            } catch (RemoteException e) {
-                // Status check failed, don't update cache
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @param c Callback that will fire when the feature status has changed.
-     */
-    public void setStatusCallback(INotifyStatusChanged c) {
-        mStatusCallback = c;
-    }
-
-    @Override
-    public boolean isBinderAlive() {
-        return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
-    }
-
-    protected void checkServiceIsReady() throws RemoteException {
-        if (!isBinderReady()) {
-            throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
-        }
-    }
-
-    private IImsServiceController getServiceInterface(IBinder b) {
-        return IImsServiceController.Stub.asInterface(b);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
deleted file mode 100644
index 7ec9229..0000000
--- a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.feature.IMMTelFeature;
-import android.telephony.ims.feature.ImsFeature;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsService;
-import com.android.ims.internal.IImsUt;
-
-/**
- * Compatibility class that implements the new ImsService IMMTelFeature interface, but
- * uses the old IImsService interface to support older devices that implement the deprecated
- * opt/net/ims interface.
- * @hide
- */
-
-public class ImsServiceProxyCompat implements IMMTelFeature {
-
-    private static final int SERVICE_ID = ImsFeature.MMTEL;
-
-    protected final int mSlotId;
-    protected IBinder mBinder;
-
-    public ImsServiceProxyCompat(int slotId, IBinder binder) {
-        mSlotId = slotId;
-        mBinder = binder;
-    }
-
-    @Override
-    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent,
-                listener);
-    }
-
-    @Override
-    public void endSession(int sessionId) throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).close(sessionId);
-    }
-
-    @Override
-    public boolean isConnected(int callServiceType, int callType)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).isConnected(SERVICE_ID,  callServiceType, callType);
-    }
-
-    @Override
-    public boolean isOpened() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).isOpened(SERVICE_ID);
-    }
-
-    @Override
-    public void addRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
-    }
-
-    @Override
-    public void removeRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException {
-        // Not Implemented in old ImsService. If the registration listener becomes invalid, the
-        // ImsService will remove.
-    }
-
-    @Override
-    public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType);
-    }
-
-    @Override
-    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
-            IImsCallSessionListener listener) throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).createCallSession(sessionId, profile, listener);
-    }
-
-    @Override
-    public IImsCallSession getPendingCallSession(int sessionId, String callId)
-            throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
-    }
-
-    @Override
-    public IImsUt getUtInterface() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getUtInterface(SERVICE_ID);
-    }
-
-    @Override
-    public IImsConfig getConfigInterface() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getConfigInterface(mSlotId);
-    }
-
-    @Override
-    public void turnOnIms() throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).turnOnIms(mSlotId);
-    }
-
-    @Override
-    public void turnOffIms() throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).turnOffIms(mSlotId);
-    }
-
-    @Override
-    public IImsEcbm getEcbmInterface() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID);
-    }
-
-    @Override
-    public void setUiTTYMode(int uiTtyMode, Message onComplete)
-            throws RemoteException {
-        checkBinderConnection();
-        getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
-    }
-
-    @Override
-    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-        checkBinderConnection();
-        return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID);
-    }
-
-    /**
-     * Base implementation, always returns READY for compatibility with old ImsService.
-     */
-    public int getFeatureStatus() {
-        return ImsFeature.STATE_READY;
-    }
-
-    /**
-     * @return false if the binder connection is no longer alive.
-     */
-    public boolean isBinderAlive() {
-        return mBinder != null && mBinder.isBinderAlive();
-    }
-
-    /**
-     * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
-     * method returns false, it doesn't mean that the Binder connection is not available (use
-     * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
-     * at this time.
-     *
-     * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
-     * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
-     */
-    public boolean isBinderReady() {
-        return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
-    }
-
-    private IImsService getServiceInterface(IBinder b) {
-        return IImsService.Stub.asInterface(b);
-    }
-
-    protected void checkBinderConnection() throws RemoteException {
-        if (!isBinderAlive()) {
-            throw new RemoteException("ImsServiceProxy is not available for that feature.");
-        }
-    }
-}
diff --git a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
deleted file mode 100644
index d65e27e..0000000
--- a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.feature;
-
-import android.app.PendingIntent;
-import android.os.Message;
-import android.os.RemoteException;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * MMTel interface for an ImsService. When updating this interface, ensure that base implementations
- * of your changes are also present in MMTelFeature for compatibility with older versions of the
- * MMTel feature.
- * @hide
- */
-
-public interface IMMTelFeature {
-
-    /**
-     * Notifies the MMTel feature that you would like to start a session. This should always be
-     * done before making/receiving IMS calls. The IMS service will register the device to the
-     * operator's network with the credentials (from ISIM) periodically in order to receive calls
-     * from the operator's network. When the IMS service receives a new call, it will send out an
-     * intent with the provided action string. The intent contains a call ID extra
-     * {@link IImsCallSession#getCallId} and it can be used to take a call.
-     *
-     * @param incomingCallIntent When an incoming call is received, the IMS service will call
-     * {@link PendingIntent#send} to send back the intent to the caller with
-     * {@link #INCOMING_CALL_RESULT_CODE} as the result code and the intent to fill in the call ID;
-     * It cannot be null.
-     * @param listener To listen to IMS registration events; It cannot be null
-     * @return an integer (greater than 0) representing the session id associated with the session
-     * that has been started.
-     */
-    int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
-            throws RemoteException;
-
-    /**
-     * End a previously started session using the associated sessionId.
-     * @param sessionId an integer (greater than 0) representing the ongoing session. See
-     * {@link #startSession}.
-     */
-    void endSession(int sessionId) throws RemoteException;
-
-    /**
-     * Checks if the IMS service has successfully registered to the IMS network with the specified
-     * service & call type.
-     *
-     * @param callServiceType a service type that is specified in {@link ImsCallProfile}
-     *        {@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_N_VIDEO}
-     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
-     *        {@link ImsCallProfile#CALL_TYPE_VT}
-     *        {@link ImsCallProfile#CALL_TYPE_VS}
-     * @return true if the specified service id is connected to the IMS network; false otherwise
-     * @throws RemoteException
-     */
-    boolean isConnected(int callServiceType, int callType) throws RemoteException;
-
-    /**
-     * Checks if the specified IMS service is opened.
-     *
-     * @return true if the specified service id is opened; false otherwise
-     */
-    boolean isOpened() throws RemoteException;
-
-    /**
-     * Add a new registration listener for the client associated with the session Id.
-     * @param listener An implementation of IImsRegistrationListener.
-     */
-    void addRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException;
-
-    /**
-     * Remove a previously registered listener using {@link #addRegistrationListener} for the client
-     * associated with the session Id.
-     * @param listener A previously registered IImsRegistrationListener
-     */
-    void removeRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException;
-
-    /**
-     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
-     *
-     * @param sessionId a session id which is obtained from {@link #startSession}
-     * @param callServiceType 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
-     */
-    ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
-            throws RemoteException;
-
-    /**
-     * Creates a {@link ImsCallSession} with the specified call profile.
-     * Use other methods, if applicable, instead of interacting with
-     * {@link ImsCallSession} directly.
-     *
-     * @param sessionId a session id which is obtained from {@link #startSession}
-     * @param profile a call profile to make the call
-     * @param listener An implementation of IImsCallSessionListener.
-     */
-    IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
-            IImsCallSessionListener listener) throws RemoteException;
-
-    /**
-     * Retrieves the call session associated with a pending call.
-     *
-     * @param sessionId a session id which is obtained from {@link #startSession}
-     * @param callId a call id to make the call
-     */
-    IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException;
-
-    /**
-     * @return The Ut interface for the supplementary service configuration.
-     */
-    IImsUt getUtInterface() throws RemoteException;
-
-    /**
-     * @return The config interface for IMS Configuration
-     */
-    IImsConfig getConfigInterface() throws RemoteException;
-
-    /**
-     * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
-     * @param sessionId a session id which is obtained from {@link #startSession}
-     */
-    void turnOnIms() throws RemoteException;
-
-    /**
-     * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
-     * @param sessionId a session id which is obtained from {@link #startSession}
-     */
-    void turnOffIms() throws RemoteException;
-
-    /**
-     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
-     */
-    IImsEcbm getEcbmInterface() throws RemoteException;
-
-    /**
-     * Sets the current UI TTY mode for the MMTelFeature.
-     * @param uiTtyMode An integer containing the new UI TTY Mode.
-     * @param onComplete A {@link Message} to be used when the mode has been set.
-     * @throws RemoteException
-     */
-    void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException;
-
-    /**
-     * @return MultiEndpoint interface for DEP notifications
-     */
-    IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException;
-}
diff --git a/telephony/java/android/telephony/ims/feature/IRcsFeature.java b/telephony/java/android/telephony/ims/feature/IRcsFeature.java
deleted file mode 100644
index e28e1b3..0000000
--- a/telephony/java/android/telephony/ims/feature/IRcsFeature.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.feature;
-
-/**
- * Feature interface that provides access to RCS APIs. Currently empty until RCS support is added
- * in the framework.
- * @hide
- */
-
-public interface IRcsFeature {
-}
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
index a71f0bf..758c379 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
@@ -32,90 +32,183 @@
 import java.util.List;
 
 /**
- * Base implementation, which implements all methods in IMMTelFeature. Any class wishing to use
- * MMTelFeature should extend this class and implement all methods that the service supports.
+ * Base implementation for MMTel.
+ * Any class wishing to use MMTelFeature should extend this class and implement all methods that the
+ * service supports.
  *
  * @hide
  */
 
-public class MMTelFeature extends ImsFeature implements IMMTelFeature {
+public class MMTelFeature extends ImsFeature {
 
-    @Override
+    /**
+     * Notifies the MMTel feature that you would like to start a session. This should always be
+     * done before making/receiving IMS calls. The IMS service will register the device to the
+     * operator's network with the credentials (from ISIM) periodically in order to receive calls
+     * from the operator's network. When the IMS service receives a new call, it will send out an
+     * intent with the provided action string. The intent contains a call ID extra
+     * {@link IImsCallSession#getCallId} and it can be used to take a call.
+     *
+     * @param incomingCallIntent When an incoming call is received, the IMS service will call
+     * {@link PendingIntent#send} to send back the intent to the caller with
+     * ImsManager#INCOMING_CALL_RESULT_CODE as the result code and the intent to fill in the call
+     * ID; It cannot be null.
+     * @param listener To listen to IMS registration events; It cannot be null
+     * @return an integer (greater than 0) representing the session id associated with the session
+     * that has been started.
+     */
     public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
         return 0;
     }
 
-    @Override
+    /**
+     * End a previously started session using the associated sessionId.
+     * @param sessionId an integer (greater than 0) representing the ongoing session. See
+     * {@link #startSession}.
+     */
     public void endSession(int sessionId) {
     }
 
-    @Override
+    /**
+     * Checks if the IMS service has successfully registered to the IMS network with the specified
+     * service & call type.
+     *
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@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_N_VIDEO}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     * @return true if the specified service id is connected to the IMS network; false otherwise
+     */
     public boolean isConnected(int callSessionType, int callType) {
         return false;
     }
 
-    @Override
+    /**
+     * Checks if the specified IMS service is opened.
+     *
+     * @return true if the specified service id is opened; false otherwise
+     */
     public boolean isOpened() {
         return false;
     }
 
-    @Override
+    /**
+     * Add a new registration listener for the client associated with the session Id.
+     * @param listener An implementation of IImsRegistrationListener.
+     */
     public void addRegistrationListener(IImsRegistrationListener listener) {
     }
 
-    @Override
+    /**
+     * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+     * associated with the session Id.
+     * @param listener A previously registered IImsRegistrationListener
+     */
     public void removeRegistrationListener(IImsRegistrationListener listener) {
     }
 
-    @Override
+    /**
+     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @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 sessionId, int callSessionType, int callType) {
         return null;
     }
 
-    @Override
+    /**
+     * Creates a {@link ImsCallSession} with the specified call profile.
+     * Use other methods, if applicable, instead of interacting with
+     * {@link ImsCallSession} directly.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @param profile a call profile to make the call
+     * @param listener An implementation of IImsCallSessionListener.
+     */
     public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
             IImsCallSessionListener listener) {
         return null;
     }
 
-    @Override
+    /**
+     * Retrieves the call session associated with a pending call.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @param callId a call id to make the call
+     */
     public IImsCallSession getPendingCallSession(int sessionId, String callId) {
         return null;
     }
 
-    @Override
+    /**
+     * @return The Ut interface for the supplementary service configuration.
+     */
     public IImsUt getUtInterface() {
         return null;
     }
 
-    @Override
+    /**
+     * @return The config interface for IMS Configuration
+     */
     public IImsConfig getConfigInterface() {
         return null;
     }
 
-    @Override
+    /**
+     * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+     */
     public void turnOnIms() {
     }
 
-    @Override
+    /**
+     * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+     */
     public void turnOffIms() {
     }
 
-    @Override
+    /**
+     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+     */
     public IImsEcbm getEcbmInterface() {
         return null;
     }
 
-    @Override
+    /**
+     * Sets the current UI TTY mode for the MMTelFeature.
+     * @param uiTtyMode An integer containing the new UI TTY Mode.
+     * @param onComplete A {@link Message} to be used when the mode has been set.
+     */
     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
     }
 
-    @Override
+    /**
+     * @return MultiEndpoint interface for DEP notifications
+     */
     public IImsMultiEndpoint getMultiEndpointInterface() {
         return null;
     }
 
-    @Override
+    /**
+     * {@inheritDoc}
+     */
     public void onFeatureRemoved() {
 
     }
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 9cddc1b..332cca3 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -18,11 +18,11 @@
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
- * this class and provide implementations of the IRcsFeature methods that they support.
+ * this class and provide implementations of the RcsFeature methods that they support.
  * @hide
  */
 
-public class RcsFeature extends ImsFeature implements IRcsFeature {
+public class RcsFeature extends ImsFeature {
 
     public RcsFeature() {
         super();
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index 892fbf0..9f60cc3 100644
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -38,7 +38,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
+    @IntDef(flag = true, value = {ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
     public @interface FilterFlag {}
 
     /**
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index 0d737b5..e064adb 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -17,10 +17,13 @@
 package android.telephony.mbms;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Describes a single file that is available over MBMS.
  */
@@ -47,6 +50,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public FileInfo(Uri uri, String mimeType) {
         this.uri = uri;
         this.mimeType = mimeType;
@@ -82,4 +86,23 @@
     public String getMimeType() {
         return mimeType;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        FileInfo fileInfo = (FileInfo) o;
+        return Objects.equals(uri, fileInfo.uri) &&
+                Objects.equals(mimeType, fileInfo.mimeType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(uri, mimeType);
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 9af1eb9..9ef188c 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -165,16 +165,16 @@
                 Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
                 return false;
             }
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
+                Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
+                return false;
+            }
             // We do not need to verify below extras if the result is not success.
             if (MbmsDownloadSession.RESULT_SUCCESSFUL !=
                     intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
                     MbmsDownloadSession.RESULT_CANCELLED)) {
                 return true;
             }
-            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
-                Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
-                return false;
-            }
             if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
                 Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
                 return false;
@@ -242,10 +242,12 @@
         int result = intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
                 MbmsDownloadSession.RESULT_CANCELLED);
         intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT, result);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
 
         if (result != MbmsDownloadSession.RESULT_SUCCESSFUL) {
             Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
             context.sendBroadcast(intentForApp);
+            setResultCode(RESULT_OK);
             return;
         }
 
@@ -273,7 +275,6 @@
         intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
                 stagedFileLocation);
         intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
-        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
 
         context.sendBroadcast(intentForApp);
         setResultCode(RESULT_OK);
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java
index 187e9ee..dd20a69 100644
--- a/telephony/java/android/telephony/mbms/UriPathPair.java
+++ b/telephony/java/android/telephony/mbms/UriPathPair.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ContentResolver;
 import android.net.Uri;
 import android.os.Parcel;
@@ -29,6 +30,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public final class UriPathPair implements Parcelable {
     private final Uri mFilePathUri;
     private final Uri mContentUri;
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index a43f122..f1cac8c 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms.vendor;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +35,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public class VendorUtils {
 
     /**
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index bb06d7e..f1e2262 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -31,7 +31,7 @@
 import android.os.Message;
 
 /**
- * See ImsService and IMMTelFeature for more information.
+ * See ImsService and MMTelFeature for more information.
  * {@hide}
  */
 interface IImsServiceController {
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index e9c5461..ac16139 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -45,7 +45,6 @@
     void onVoLteServiceStateChanged(in VoLteServiceState lteState);
     void onVoiceActivationStateChanged(int activationState);
     void onDataActivationStateChanged(int activationState);
-    void onOemHookRawEvent(in byte[] rawData);
     void onCarrierNetworkChange(in boolean active);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2ac11b5..3cc9bde 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1000,17 +1000,6 @@
             in List<String> cdmaNonRoamingList);
 
     /**
-     * Returns the result and response from RIL for oem request
-     *
-     * @param oemReq the data is sent to ril.
-     * @param oemResp the respose data from RIL.
-     * @return negative value request was not handled or get error
-     *         0 request was handled succesfully, but no response data
-     *         positive value success, data length of response
-     */
-    int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
-
-    /**
      * Check if any mobile Radios need to be shutdown.
      *
      * @return true is any mobile radio needs to be shutdown
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 2c2206c..75d8f3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -67,7 +67,6 @@
     void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
     void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
             int activationState, int activationType);
-    void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
     void notifySubscriptionInfoChanged();
     void notifyCarrierNetworkChange(in boolean active);
 }
diff --git a/tests/FeatureSplit/feature1/Android.mk b/tests/FeatureSplit/feature1/Android.mk
index b3ea97b..e6ba5c2 100644
--- a/tests/FeatureSplit/feature1/Android.mk
+++ b/tests/FeatureSplit/feature1/Android.mk
@@ -26,6 +26,6 @@
 LOCAL_RES_LIBRARIES := FeatureSplitBase
 
 LOCAL_AAPT_FLAGS += --package-id 0x80
-LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.test.split.feature
+LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.one
 
 include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature1/AndroidManifest.xml b/tests/FeatureSplit/feature1/AndroidManifest.xml
index 4e7d151..b87361f 100644
--- a/tests/FeatureSplit/feature1/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature1/AndroidManifest.xml
@@ -15,13 +15,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.test.split.feature.one"
+    package="com.android.test.split.feature"
     featureSplit="feature1">
 
     <uses-sdk android:minSdkVersion="21" />
 
     <application>
-        <activity android:name=".One" android:label="Feature One">
+        <activity android:name=".one.One" android:label="Feature One">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/FeatureSplit/feature1/res/values/values.xml b/tests/FeatureSplit/feature1/res/values/values.xml
index 0e3e73c..7d58865 100644
--- a/tests/FeatureSplit/feature1/res/values/values.xml
+++ b/tests/FeatureSplit/feature1/res/values/values.xml
@@ -20,7 +20,7 @@
     <integer name="test_integer2">200</integer>
     <color name="test_color2">#00ff00</color>
     <string-array name="string_array2">
-        <item>@*com.android.test.split.feature:string/app_title</item>
+        <item>@string/app_title</item>
     </string-array>
 </resources>
 
diff --git a/tests/FeatureSplit/feature2/Android.mk b/tests/FeatureSplit/feature2/Android.mk
index e2fd903..c8e8609 100644
--- a/tests/FeatureSplit/feature2/Android.mk
+++ b/tests/FeatureSplit/feature2/Android.mk
@@ -26,6 +26,6 @@
 LOCAL_RES_LIBRARIES := FeatureSplitBase
 
 LOCAL_AAPT_FLAGS += --package-id 0x81
-LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.test.split.feature
+LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.two
 
 include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature2/AndroidManifest.xml b/tests/FeatureSplit/feature2/AndroidManifest.xml
index bfe6f38..abd0b5e 100644
--- a/tests/FeatureSplit/feature2/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature2/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.test.split.feature.two"
+    package="com.android.test.split.feature"
     featureSplit="feature2">
 
     <uses-sdk android:minSdkVersion="21" />
diff --git a/tests/FeatureSplit/feature2/res/values/values.xml b/tests/FeatureSplit/feature2/res/values/values.xml
index 2fa6f90..af5ed1b 100644
--- a/tests/FeatureSplit/feature2/res/values/values.xml
+++ b/tests/FeatureSplit/feature2/res/values/values.xml
@@ -18,7 +18,7 @@
     <integer name="test_integer3">300</integer>
     <color name="test_color3">#0000ff</color>
     <string-array name="string_array3">
-        <item>@*com.android.test.split.feature:string/app_title</item>
+        <item>@string/app_title</item>
     </string-array>
 </resources>
 
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 0824c26..60327e5 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -10,25 +10,11 @@
 
 # use appcompat/support lib from the tree, so improvements/
 # regressions are reflected in test data
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/res \
-    frameworks/support/core-ui/res \
-    frameworks/support/design/res \
-    frameworks/support/v7/appcompat/res \
-    frameworks/support/v7/cardview/res \
-    frameworks/support/v7/recyclerview/res \
-    frameworks/support/v17/leanback/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_AAPT_FLAGS := \
-    --auto-add-overlay \
-    --extra-packages android.support.coreui \
-    --extra-packages android.support.design \
-    --extra-packages android.support.v7.appcompat \
-    --extra-packages android.support.v7.cardview \
-    --extra-packages android.support.v7.recyclerview \
-    --extra-packages android.support.v17.leanback
+LOCAL_USE_AAPT2 := true
 
-LOCAL_STATIC_JAVA_LIBRARIES := \
+LOCAL_STATIC_ANDROID_LIBRARIES := \
     android-support-design \
     android-support-v4 \
     android-support-v7-appcompat \
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 3fa9c3a..fcbb9da 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -19,12 +19,14 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
 
 import android.net.MacAddress.MacAddressType;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import java.util.Arrays;
+import java.util.Random;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -64,10 +66,139 @@
             String msg = String.format("expected type of %s to be %s, but got %s",
                     Arrays.toString(t.addr), t.expected, got);
             assertEquals(msg, t.expected, got);
+
+            if (got != null) {
+                assertEquals(got, new MacAddress(t.addr).addressType());
+            }
         }
     }
 
-    static byte[] toByteArray(int[] in) {
+    @Test
+    public void testIsMulticastAddress() {
+        MacAddress[] multicastAddresses = {
+            MacAddress.BROADCAST_ADDRESS,
+            new MacAddress("07:00:d3:56:8a:c4"),
+            new MacAddress("33:33:aa:bb:cc:dd"),
+        };
+        MacAddress[] unicastAddresses = {
+            MacAddress.ALL_ZEROS_ADDRESS,
+            new MacAddress("00:01:44:55:66:77"),
+            new MacAddress("08:00:22:33:44:55"),
+            new MacAddress("06:00:00:00:00:00"),
+        };
+
+        for (MacAddress mac : multicastAddresses) {
+            String msg = mac.toString() + " expected to be a multicast address";
+            assertTrue(msg, mac.isMulticastAddress());
+        }
+        for (MacAddress mac : unicastAddresses) {
+            String msg = mac.toString() + " expected not to be a multicast address";
+            assertFalse(msg, mac.isMulticastAddress());
+        }
+    }
+
+    @Test
+    public void testIsLocallyAssignedAddress() {
+        MacAddress[] localAddresses = {
+            new MacAddress("06:00:00:00:00:00"),
+            new MacAddress("07:00:d3:56:8a:c4"),
+            new MacAddress("33:33:aa:bb:cc:dd"),
+        };
+        MacAddress[] universalAddresses = {
+            new MacAddress("00:01:44:55:66:77"),
+            new MacAddress("08:00:22:33:44:55"),
+        };
+
+        for (MacAddress mac : localAddresses) {
+            String msg = mac.toString() + " expected to be a locally assigned address";
+            assertTrue(msg, mac.isLocallyAssigned());
+        }
+        for (MacAddress mac : universalAddresses) {
+            String msg = mac.toString() + " expected not to be globally unique address";
+            assertFalse(msg, mac.isLocallyAssigned());
+        }
+    }
+
+    @Test
+    public void testMacAddressConversions() {
+        final int iterations = 10000;
+        for (int i = 0; i < iterations; i++) {
+            MacAddress mac = MacAddress.getRandomAddress();
+
+            String stringRepr = mac.toString();
+            byte[] bytesRepr = mac.toByteArray();
+
+            assertEquals(mac, new MacAddress(stringRepr));
+            assertEquals(mac, new MacAddress(bytesRepr));
+        }
+    }
+
+    @Test
+    public void testMacAddressRandomGeneration() {
+        final int iterations = 1000;
+        final String expectedAndroidOui = "da:a1:19";
+        for (int i = 0; i < iterations; i++) {
+            MacAddress mac = MacAddress.getRandomAddress();
+            String stringRepr = mac.toString();
+
+            assertTrue(stringRepr + " expected to be a locally assigned address",
+                    mac.isLocallyAssigned());
+            assertTrue(stringRepr + " expected to begin with " + expectedAndroidOui,
+                    stringRepr.startsWith(expectedAndroidOui));
+        }
+
+        final Random r = new Random();
+        final String anotherOui = "24:5f:78";
+        final String expectedLocalOui = "26:5f:78";
+        final MacAddress base = new MacAddress(anotherOui + ":0:0:0");
+        for (int i = 0; i < iterations; i++) {
+            MacAddress mac = MacAddress.getRandomAddress(base, r);
+            String stringRepr = mac.toString();
+
+            assertTrue(stringRepr + " expected to be a locally assigned address",
+                    mac.isLocallyAssigned());
+            assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
+                    stringRepr.startsWith(expectedLocalOui));
+        }
+    }
+
+    @Test
+    public void testConstructorInputValidation() {
+        String[] invalidStringAddresses = {
+            null,
+            "",
+            "abcd",
+            "1:2:3:4:5",
+            "1:2:3:4:5:6:7",
+            "10000:2:3:4:5:6",
+        };
+
+        for (String s : invalidStringAddresses) {
+            try {
+                MacAddress mac = new MacAddress(s);
+                fail("new MacAddress(" + s + ") should have failed, but returned " + mac);
+            } catch (IllegalArgumentException excepted) {
+            }
+        }
+
+        byte[][] invalidBytesAddresses = {
+            null,
+            {},
+            {1,2,3,4,5},
+            {1,2,3,4,5,6,7},
+        };
+
+        for (byte[] b : invalidBytesAddresses) {
+            try {
+                MacAddress mac = new MacAddress(b);
+                fail("new MacAddress(" + Arrays.toString(b)
+                        + ") should have failed, but returned " + mac);
+            } catch (IllegalArgumentException excepted) {
+            }
+        }
+    }
+
+    static byte[] toByteArray(int... in) {
         byte[] out = new byte[in.length];
         for (int i = 0; i < in.length; i++) {
             out[i] = (byte) in[i];
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 9e97d84b..5c031eb 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -64,6 +64,13 @@
         return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
     }
 
+    private static final byte[] AEAD_KEY = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+        0x73, 0x61, 0x6C, 0x74
+    };
     private static final byte[] CRYPT_KEY = {
         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
@@ -87,7 +94,7 @@
     private static final IpSecAlgorithm CRYPT_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
     private static final IpSecAlgorithm AEAD_ALGO =
-            new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, CRYPT_KEY, CRYPT_KEY.length * 4);
+            new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
 
     private static final int[] DIRECTIONS =
             new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
@@ -262,7 +269,7 @@
                         eq(new byte[] {}),
                         eq(0),
                         eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
-                        eq(CRYPT_KEY),
+                        eq(AEAD_KEY),
                         anyInt(),
                         anyInt(),
                         anyInt(),
@@ -283,7 +290,7 @@
                         eq(new byte[] {}),
                         eq(0),
                         eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
-                        eq(CRYPT_KEY),
+                        eq(AEAD_KEY),
                         anyInt(),
                         anyInt(),
                         anyInt(),
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index a65bb24..0656c5f 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -337,7 +337,6 @@
     public void testNetworkEventSerialization() {
         ConnectivityMetricsEvent ev = describeIpEvent(
                 aType(NetworkEvent.class),
-                anInt(100),
                 anInt(5),
                 aLong(20410));
 
@@ -352,9 +351,6 @@
                 "  network_event <",
                 "    event_type: 5",
                 "    latency_ms: 20410",
-                "    network_id <",
-                "      network_id: 100",
-                "    >",
                 "  >",
                 ">",
                 "version: 2\n");
@@ -508,6 +504,13 @@
         stats.rootWakeups = 2;
         stats.systemWakeups = 3;
         stats.noUidWakeups = 3;
+        stats.l2UnicastCount = 5;
+        stats.l2MulticastCount = 1;
+        stats.l2BroadcastCount = 2;
+        stats.ethertypes.put(0x800, 3);
+        stats.ethertypes.put(0x86dd, 3);
+        stats.ipNextHeaders.put(6, 5);
+
 
         IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats);
         String want = String.join("\n",
@@ -521,6 +524,21 @@
                 "  wakeup_stats <",
                 "    application_wakeups: 5",
                 "    duration_sec: 0",
+                "    ethertype_counts <",
+                "      key: 2048",
+                "      value: 3",
+                "    >",
+                "    ethertype_counts <",
+                "      key: 34525",
+                "      value: 3",
+                "    >",
+                "    ip_next_header_counts <",
+                "      key: 6",
+                "      value: 5",
+                "    >",
+                "    l2_broadcast_count: 2",
+                "    l2_multicast_count: 1",
+                "    l2_unicast_count: 5",
                 "    no_uid_wakeups: 3",
                 "    non_application_wakeups: 1",
                 "    root_wakeups: 2",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index b48ff8d..10d6deb 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -78,6 +78,9 @@
     private static final String EXAMPLE_IPV4 = "192.0.2.1";
     private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
 
+    private static final byte[] MAC_ADDR =
+            {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
+
     @Mock Context mCtx;
     @Mock IIpConnectivityMetrics mMockService;
     @Mock ConnectivityManager mCm;
@@ -351,13 +354,21 @@
         dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
 
         // iface, uid
-        wakeupEvent("wlan0", 1000);
-        wakeupEvent("rmnet0", 10123);
-        wakeupEvent("wlan0", 1000);
-        wakeupEvent("rmnet0", 10008);
-        wakeupEvent("wlan0", -1);
-        wakeupEvent("wlan0", 10008);
-        wakeupEvent("rmnet0", 1000);
+        final byte[] mac = {0x48, 0x7c, 0x2b, 0x6a, 0x3e, 0x4b};
+        final String srcIp = "192.168.2.1";
+        final String dstIp = "192.168.2.23";
+        final int sport = 2356;
+        final int dport = 13489;
+        final long now = 1001L;
+        final int v4 = 0x800;
+        final int tcp = 6;
+        final int udp = 17;
+        wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
+        wakeupEvent("wlan0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
+        wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+        wakeupEvent("wlan0", 10008, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+        wakeupEvent("wlan0", -1, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+        wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
 
         long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
         final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
@@ -560,34 +571,33 @@
                 ">",
                 "events <",
                 "  if_name: \"\"",
-                "  link_layer: 2",
-                "  network_id: 0",
-                "  time_ms: 0",
-                "  transports: 0",
-                "  wakeup_stats <",
-                "    application_wakeups: 2",
-                "    duration_sec: 0",
-                "    no_uid_wakeups: 0",
-                "    non_application_wakeups: 0",
-                "    root_wakeups: 0",
-                "    system_wakeups: 1",
-                "    total_wakeups: 3",
-                "  >",
-                ">",
-                "events <",
-                "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 0",
                 "  time_ms: 0",
                 "  transports: 0",
                 "  wakeup_stats <",
-                "    application_wakeups: 1",
+                "    application_wakeups: 3",
                 "    duration_sec: 0",
+                "    ethertype_counts <",
+                "      key: 2048",
+                "      value: 6",
+                "    >",
+                "    ip_next_header_counts <",
+                "      key: 6",
+                "      value: 3",
+                "    >",
+                "    ip_next_header_counts <",
+                "      key: 17",
+                "      value: 3",
+                "    >",
+                "    l2_broadcast_count: 0",
+                "    l2_multicast_count: 0",
+                "    l2_unicast_count: 6",
                 "    no_uid_wakeups: 1",
                 "    non_application_wakeups: 0",
                 "    root_wakeups: 0",
                 "    system_wakeups: 2",
-                "    total_wakeups: 4",
+                "    total_wakeups: 6",
                 "  >",
                 ">",
                 "version: 2\n");
@@ -610,9 +620,10 @@
         mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
     }
 
-    void wakeupEvent(String iface, int uid) throws Exception {
+    void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
+            String dstIp, int sport, int dport, long now) throws Exception {
         String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
-        mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
+        mNetdListener.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
     }
 
     NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) {
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 83194d9..67805c9 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -61,7 +61,10 @@
     private static final String EXAMPLE_IPV4 = "192.0.2.1";
     private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
 
-    NetdEventListenerService mNetdEventListenerService;
+    private static final byte[] MAC_ADDR =
+            {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
+
+    NetdEventListenerService mService;
     ConnectivityManager mCm;
 
     @Before
@@ -75,28 +78,49 @@
         when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
         when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
 
-        mNetdEventListenerService = new NetdEventListenerService(mCm);
+        mService = new NetdEventListenerService(mCm);
     }
 
     @Test
     public void testWakeupEventLogging() throws Exception {
         final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
+        final long now = System.currentTimeMillis();
+        final String iface = "wlan0";
+        final byte[] mac = MAC_ADDR;
+        final String srcIp = "192.168.2.1";
+        final String dstIp = "192.168.2.23";
+        final String srcIp6 = "2001:db8:4:fd00:a585:13d1:6a23:4fb4";
+        final String dstIp6 = "2001:db8:4006:807::200a";
+        final int sport = 2356;
+        final int dport = 13489;
+
+        final int v4 = 0x800;
+        final int v6 = 0x86dd;
+        final int tcp = 6;
+        final int udp = 17;
+        final int icmp6 = 58;
 
         // Baseline without any event
         String[] baseline = listNetdEvent();
 
-        long now = System.currentTimeMillis();
-        String prefix = "iface:wlan0";
-        int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
-        for (int uid : uids) {
-            mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
-        }
+        int[] uids = {10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004};
+        wakeupEvent(iface, uids[0], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent(iface, uids[1], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent(iface, uids[2], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent(iface, uids[3], v4, icmp6, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent(iface, uids[4], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent(iface, uids[5], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent(iface, uids[6], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent(iface, uids[7], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent(iface, uids[8], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
 
         String[] events2 = remove(listNetdEvent(), baseline);
         int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
         assertEquals(expectedLength2, events2.length);
         assertContains(events2[0], "WakeupStats");
         assertContains(events2[0], "wlan0");
+        assertContains(events2[0], "0x800");
+        assertContains(events2[0], "0x86dd");
         for (int i = 0; i < uids.length; i++) {
             String got = events2[i+1];
             assertContains(got, "WakeupEvent");
@@ -107,7 +131,7 @@
         int uid = 20000;
         for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
             long ts = now + 10;
-            mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
+            wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, ts);
         }
 
         String[] events3 = remove(listNetdEvent(), baseline);
@@ -123,7 +147,7 @@
         }
 
         uid = 45678;
-        mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+        wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, now);
 
         String[] events4 = remove(listNetdEvent(), baseline);
         String lastEvent = events4[events4.length - 1];
@@ -134,21 +158,36 @@
 
     @Test
     public void testWakeupStatsLogging() throws Exception {
-        wakeupEvent("wlan0", 1000);
-        wakeupEvent("rmnet0", 10123);
-        wakeupEvent("wlan0", 1000);
-        wakeupEvent("rmnet0", 10008);
-        wakeupEvent("wlan0", -1);
-        wakeupEvent("wlan0", 10008);
-        wakeupEvent("rmnet0", 1000);
-        wakeupEvent("wlan0", 10004);
-        wakeupEvent("wlan0", 1000);
-        wakeupEvent("wlan0", 0);
-        wakeupEvent("wlan0", -1);
-        wakeupEvent("rmnet0", 10052);
-        wakeupEvent("wlan0", 0);
-        wakeupEvent("rmnet0", 1000);
-        wakeupEvent("wlan0", 1010);
+        final byte[] mac = MAC_ADDR;
+        final String srcIp = "192.168.2.1";
+        final String dstIp = "192.168.2.23";
+        final String srcIp6 = "2401:fa00:4:fd00:a585:13d1:6a23:4fb4";
+        final String dstIp6 = "2404:6800:4006:807::200a";
+        final int sport = 2356;
+        final int dport = 13489;
+        final long now = 1001L;
+
+        final int v4 = 0x800;
+        final int v6 = 0x86dd;
+        final int tcp = 6;
+        final int udp = 17;
+        final int icmp6 = 58;
+
+        wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent("rmnet0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent("rmnet0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent("rmnet0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent("wlan0", 10004, v4, udp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent("wlan0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent("rmnet0", 10052, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+        wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent("rmnet0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+        wakeupEvent("wlan0", 1010, v4, udp, mac, srcIp, dstIp, sport, dport, now);
 
         String got = flushStatistics();
         String want = String.join("\n",
@@ -162,6 +201,21 @@
                 "  wakeup_stats <",
                 "    application_wakeups: 3",
                 "    duration_sec: 0",
+                "    ethertype_counts <",
+                "      key: 2048",
+                "      value: 4",
+                "    >",
+                "    ethertype_counts <",
+                "      key: 34525",
+                "      value: 1",
+                "    >",
+                "    ip_next_header_counts <",
+                "      key: 6",
+                "      value: 5",
+                "    >",
+                "    l2_broadcast_count: 0",
+                "    l2_multicast_count: 0",
+                "    l2_unicast_count: 5",
                 "    no_uid_wakeups: 0",
                 "    non_application_wakeups: 0",
                 "    root_wakeups: 0",
@@ -178,6 +232,29 @@
                 "  wakeup_stats <",
                 "    application_wakeups: 2",
                 "    duration_sec: 0",
+                "    ethertype_counts <",
+                "      key: 2048",
+                "      value: 5",
+                "    >",
+                "    ethertype_counts <",
+                "      key: 34525",
+                "      value: 5",
+                "    >",
+                "    ip_next_header_counts <",
+                "      key: 6",
+                "      value: 3",
+                "    >",
+                "    ip_next_header_counts <",
+                "      key: 17",
+                "      value: 5",
+                "    >",
+                "    ip_next_header_counts <",
+                "      key: 58",
+                "      value: 2",
+                "    >",
+                "    l2_broadcast_count: 0",
+                "    l2_multicast_count: 0",
+                "    l2_unicast_count: 10",
                 "    no_uid_wakeups: 2",
                 "    non_application_wakeups: 1",
                 "    root_wakeups: 2",
@@ -401,7 +478,7 @@
     Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
         return new Thread(() -> {
             try {
-                mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
+                mService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
             } catch (Exception e) {
                 fail(e.toString());
             }
@@ -409,12 +486,13 @@
     }
 
     void dnsEvent(int netId, int type, int result, int latency) throws Exception {
-        mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
+        mService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
     }
 
-    void wakeupEvent(String iface, int uid) throws Exception {
+    void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
+            String dstIp, int sport, int dport, long now) throws Exception {
         String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
-        mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, 0);
+        mService.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
     }
 
     void asyncDump(long durationMs) throws Exception {
@@ -422,7 +500,7 @@
         final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
         new Thread(() -> {
             while (System.currentTimeMillis() < stop) {
-                mNetdEventListenerService.list(pw);
+                mService.list(pw);
             }
         }).start();
     }
@@ -431,7 +509,7 @@
     String flushStatistics() throws Exception {
         IpConnectivityMetrics metricsService =
                 new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
-        metricsService.mNetdListener = mNetdEventListenerService;
+        metricsService.mNetdListener = mService;
 
         StringWriter buffer = new StringWriter();
         PrintWriter writer = new PrintWriter(buffer);
@@ -453,7 +531,7 @@
     String[] listNetdEvent() throws Exception {
         StringWriter buffer = new StringWriter();
         PrintWriter writer = new PrintWriter(buffer);
-        mNetdEventListenerService.list(writer);
+        mService.list(writer);
         return buffer.toString().split("\\n");
     }
 
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 921d853..5981401 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -235,8 +235,7 @@
         return false;
       }
     } else {
-      uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
-      if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
+      if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
         return false;
       }
     }
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index b38d259..a782cd3 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -118,7 +118,7 @@
 bool Reference::Flatten(android::Res_value* out_value) const {
   const ResourceId resid = id.value_or_default(ResourceId(0));
   const bool dynamic = resid.is_valid_dynamic() && resid.package_id() != kFrameworkPackageId &&
-                       resid.package_id() != kAppPackageId;
+                       resid.package_id() < kAppPackageId;
 
   if (reference_type == Reference::Type::kResource) {
     if (dynamic) {
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 89ae9e8..2bd2405 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -26,49 +26,52 @@
 #include "format/binary/TableFlattener.h"
 #include "format/binary/XmlFlattener.h"
 #include "format/proto/ProtoDeserialize.h"
+#include "format/proto/ProtoSerialize.h"
 #include "io/BigBufferStream.h"
 #include "io/Util.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
+#include "util/Util.h"
 
 using ::android::StringPiece;
+using ::android::base::StringPrintf;
 using ::std::unique_ptr;
 using ::std::vector;
 
 namespace aapt {
 
-static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml,
-                       const std::string& entry_path, bool utf16, IArchiveWriter* writer) {
-  BigBuffer buffer(4096);
-  XmlFlattenerOptions options = {};
-  options.use_utf16 = utf16;
-  XmlFlattener flattener(&buffer, options);
-  if (!flattener.Consume(context, &xml)) {
-    return false;
-  }
-  io::BigBufferInputStream input_stream(&buffer);
-  return io::CopyInputStreamToArchive(context, &input_stream, entry_path, ArchiveEntry::kCompress,
-                                      writer);
-}
+class IApkSerializer {
+ public:
+  IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
 
-bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk,
-                                const TableFlattenerOptions& options, IArchiveWriter* writer) {
-  if (!FlattenXml(context, *apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
+  virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+                            IArchiveWriter* writer) = 0;
+  virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
+  virtual bool SerializeFile(const FileReference* file, IArchiveWriter* writer) = 0;
+
+  virtual ~IApkSerializer() = default;
+
+ protected:
+  IAaptContext* context_;
+  Source source_;
+};
+
+bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer* serializer,
+                IArchiveWriter* writer) {
+  if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
+    context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                     << "failed to serialize AndroidManifest.xml");
     return false;
   }
 
-  BigBuffer buffer(4096);
-  TableFlattener table_flattener(options, &buffer);
-  if (!table_flattener.Consume(context, apk->GetResourceTable())) {
+  // Resource table
+  if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) {
+    context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                     << "failed to serialize the resource table");
     return false;
   }
 
-  io::BigBufferInputStream input_stream(&buffer);
-  if (!io::CopyInputStreamToArchive(context, &input_stream, kApkResourceTablePath,
-                                    ArchiveEntry::kAlign, writer)) {
-    return false;
-  }
-
+  // Resources
   for (const auto& package : apk->GetResourceTable()->packages) {
     for (const auto& type : package->types) {
       for (const auto& entry : type->entries) {
@@ -76,59 +79,187 @@
           const FileReference* file = ValueCast<FileReference>(config_value->value.get());
           if (file != nullptr) {
             if (file->file == nullptr) {
-              context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource())
-                                              << "no file associated with " << *file);
+              context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                               << "no file associated with " << *file);
               return false;
             }
 
-            if (file->type == ResourceFile::Type::kProtoXml) {
-              unique_ptr<io::InputStream> in = file->file->OpenInputStream();
-              if (in == nullptr) {
-                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                 << "failed to open file " << *file->path);
-                return false;
-              }
-
-              pb::XmlNode pb_node;
-              io::ZeroCopyInputAdaptor adaptor(in.get());
-              if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
-                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                 << "failed to parse proto XML " << *file->path);
-                return false;
-              }
-
-              std::string error;
-              unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
-              if (xml == nullptr) {
-                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                 << "failed to deserialize proto XML "
-                                                 << *file->path << ": " << error);
-                return false;
-              }
-
-              if (!FlattenXml(context, *xml, *file->path, false /*utf16*/, writer)) {
-                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                 << "failed to serialize XML " << *file->path);
-                return false;
-              }
-            } else {
-              if (!io::CopyFileToArchive(context, file->file, *file->path,
-                                         file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u,
-                                         writer)) {
-                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                 << "failed to copy file " << *file->path);
-                return false;
-              }
+            if (!serializer->SerializeFile(file, writer)) {
+              context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                               << "failed to serialize file " << *file->path);
+              return false;
             }
-
           } // file
         } // config_value
       } // entry
     } // type
   } // package
+
+  // Other files
+  std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
+  while (iterator->HasNext()) {
+    io::IFile* file = iterator->Next();
+
+    std::string path = file->GetSource().path;
+    // The name of the path has the format "<zip-file-name>@<path-to-file>".
+    path = path.substr(path.find('@') + 1);
+
+    // Manifest, resource table and resources have already been taken care of.
+    if (path == kAndroidManifestPath ||
+        path == kApkResourceTablePath ||
+        path == kProtoResourceTablePath ||
+        path.find("res/") == 0) {
+      continue;
+    }
+
+    if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
+      context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                       << "failed to copy file " << path);
+      return false;
+    }
+  }
+
   return true;
 }
 
+
+class BinaryApkSerializer : public IApkSerializer {
+ public:
+  BinaryApkSerializer(IAaptContext* context, const Source& source,
+                   const TableFlattenerOptions& options)
+      : IApkSerializer(context, source), tableFlattenerOptions_(options) {}
+
+  bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+                    IArchiveWriter* writer) override {
+    BigBuffer buffer(4096);
+    XmlFlattenerOptions options = {};
+    options.use_utf16 = utf16;
+    XmlFlattener flattener(&buffer, options);
+    if (!flattener.Consume(context_, xml)) {
+      return false;
+    }
+
+    io::BigBufferInputStream input_stream(&buffer);
+    return io::CopyInputStreamToArchive(context_, &input_stream, path, ArchiveEntry::kCompress,
+                                        writer);
+  }
+
+  bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
+    BigBuffer buffer(4096);
+    TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
+    if (!table_flattener.Consume(context_, table)) {
+      return false;
+    }
+
+    io::BigBufferInputStream input_stream(&buffer);
+    return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
+                                        ArchiveEntry::kAlign, writer);
+  }
+
+  bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+    if (file->type == ResourceFile::Type::kProtoXml) {
+      unique_ptr<io::InputStream> in = file->file->OpenInputStream();
+      if (in == nullptr) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to open file " << *file->path);
+        return false;
+      }
+
+      pb::XmlNode pb_node;
+      io::ZeroCopyInputAdaptor adaptor(in.get());
+      if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to parse proto XML " << *file->path);
+        return false;
+      }
+
+      std::string error;
+      unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
+      if (xml == nullptr) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to deserialize proto XML "
+                                          << *file->path << ": " << error);
+        return false;
+      }
+
+      if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to serialize to binary XML: " << *file->path);
+        return false;
+      }
+    } else {
+      if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to copy file " << *file->path);
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+ private:
+  TableFlattenerOptions tableFlattenerOptions_;
+
+  DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
+};
+
+class ProtoApkSerializer : public IApkSerializer {
+ public:
+  ProtoApkSerializer(IAaptContext* context, const Source& source)
+      : IApkSerializer(context, source) {}
+
+  bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+                    IArchiveWriter* writer) override {
+    pb::XmlNode pb_node;
+    SerializeXmlResourceToPb(*xml, &pb_node);
+    return io::CopyProtoToArchive(context_, &pb_node, path, ArchiveEntry::kCompress, writer);
+  }
+
+  bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
+    pb::ResourceTable pb_table;
+    SerializeTableToPb(*table, &pb_table);
+    return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
+                                  ArchiveEntry::kCompress, writer);
+  }
+
+  bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+    if (file->type == ResourceFile::Type::kBinaryXml) {
+      std::unique_ptr<io::IData> data = file->file->OpenAsData();
+      if (!data) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                         << "failed to open file " << *file->path);
+        return false;
+      }
+
+      std::string error;
+      std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
+      if (xml == nullptr) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
+                                          << error);
+        return false;
+      }
+
+      if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to serialize to proto XML: " << *file->path);
+        return false;
+      }
+    } else {
+      if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
+        context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                          << "failed to copy file " << *file->path);
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProtoApkSerializer);
+};
+
 class Context : public IAaptContext {
  public:
   Context() : mangler_({}), symbols_(&mangler_) {
@@ -181,12 +312,20 @@
 };
 
 int Convert(const vector<StringPiece>& args) {
+
+  static const char* kOutputFormatProto = "proto";
+  static const char* kOutputFormatBinary = "binary";
+
   Context context;
   std::string output_path;
+  Maybe<std::string> output_format;
   TableFlattenerOptions options;
   Flags flags =
       Flags()
           .RequiredFlag("-o", "Output path", &output_path)
+          .OptionalFlag("--output-format", StringPrintf("Format of the output. Accepted values are "
+                        "'%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto,
+                        kOutputFormatBinary, kOutputFormatBinary), &output_format)
           .OptionalSwitch("--enable-sparse-encoding",
                           "Enables encoding sparse entries using a binary search tree.\n"
                           "This decreases APK size at the cost of resource retrieval performance.",
@@ -222,7 +361,21 @@
   if (writer == nullptr) {
     return 1;
   }
-  return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1;
+
+  unique_ptr<IApkSerializer> serializer;
+  if (!output_format || output_format.value() == kOutputFormatBinary) {
+    serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options));
+  } else if (output_format.value() == kOutputFormatProto) {
+    serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
+  } else {
+    context.GetDiagnostics()->Error(DiagMessage(path)
+                                    << "Invalid value for flag --output-format: "
+                                    << output_format.value());
+    return 1;
+  }
+
+
+  return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index b372923..66b5a7a 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -795,6 +795,9 @@
     for (auto& entry : asset_source->GetAssignedPackageIds()) {
       if (entry.first > kFrameworkPackageId && entry.first < kAppPackageId) {
         final_table_.included_packages_[entry.first] = entry.second;
+      } else if (entry.first == kAppPackageId) {
+        // Capture the included base feature package.
+        included_feature_base_ = entry.second;
       }
     }
 
@@ -1465,11 +1468,48 @@
       return false;
     }
 
-    if (!FlattenTable(table, options_.output_format, writer)) {
-      context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resource table");
-      return false;
+    // Hack to fix b/68820737.
+    // We need to modify the ResourceTable's package name, but that should NOT affect
+    // anything else being generated, which includes the Java classes.
+    // If required, the package name is modifed before flattening, and then modified back
+    // to its original name.
+    ResourceTablePackage* package_to_rewrite = nullptr;
+    if (context_->GetPackageId() > kAppPackageId &&
+        included_feature_base_ == make_value(context_->GetCompilationPackage())) {
+      // The base APK is included, and this is a feature split. If the base package is
+      // the same as this package, then we are building an old style Android Instant Apps feature
+      // split and must apply this workaround to avoid requiring namespaces support.
+      package_to_rewrite = table->FindPackage(context_->GetCompilationPackage());
+      if (package_to_rewrite != nullptr) {
+        CHECK_EQ(1u, table->packages.size()) << "can't change name of package when > 1 package";
+
+        std::string new_package_name =
+            StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
+                         app_info_.split_name.value_or_default("feature").c_str());
+
+        if (context_->IsVerbose()) {
+          context_->GetDiagnostics()->Note(
+              DiagMessage() << "rewriting resource package name for feature split to '"
+                            << new_package_name << "'");
+        }
+        package_to_rewrite->name = new_package_name;
+      }
     }
-    return true;
+
+    bool success = FlattenTable(table, options_.output_format, writer);
+
+    if (package_to_rewrite != nullptr) {
+      // Change the name back.
+      package_to_rewrite->name = context_->GetCompilationPackage();
+      if (package_to_rewrite->id) {
+        table->included_packages_.erase(package_to_rewrite->id.value());
+      }
+    }
+
+    if (!success) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resource table");
+    }
+    return success;
   }
 
   int Run(const std::vector<std::string>& input_files) {
@@ -1498,8 +1538,8 @@
       return 1;
     }
 
-    const AppInfo& app_info = maybe_app_info.value();
-    context_->SetMinSdkVersion(app_info.min_sdk_version.value_or_default(0));
+    app_info_ = maybe_app_info.value();
+    context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
 
     context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
 
@@ -1699,7 +1739,7 @@
 
         // Generate an AndroidManifest.xml for each split.
         std::unique_ptr<xml::XmlResource> split_manifest =
-            GenerateSplitManifest(app_info, *split_constraints_iter);
+            GenerateSplitManifest(app_info_, *split_constraints_iter);
 
         XmlReferenceLinker linker;
         if (!linker.Consume(context_, split_manifest.get())) {
@@ -1799,6 +1839,8 @@
   LinkContext* context_;
   ResourceTable final_table_;
 
+  AppInfo app_info_;
+
   std::unique_ptr<TableMerger> table_merger_;
 
   // A pointer to the FileCollection representing the filesystem (not archives).
@@ -1816,6 +1858,9 @@
 
   // The set of shared libraries being used, mapping their assigned package ID to package name.
   std::map<size_t, std::string> shared_libs_;
+
+  // The package name of the base application, if it is included.
+  Maybe<std::string> included_feature_base_;
 };
 
 int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 688b6a7..2bf91a5 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -256,10 +256,8 @@
 
         for (auto& entry : config_sorted_files) {
           FileReference* file_ref = entry.second;
-          uint32_t compression_flags =
-              file_ref->file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
-          if (!io::CopyFileToArchive(context_, file_ref->file, *file_ref->path, compression_flags,
-                                     writer)) {
+          if (!io::CopyFileToArchivePreserveCompression(context_, file_ref->file, *file_ref->path,
+                                                        writer)) {
             return false;
           }
         }
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index d39f43e..8b3a670 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -140,12 +140,27 @@
   return decl;
 }
 
+// Returns a copy of 'name' which conforms to the regex '[a-zA-Z]+[a-zA-Z0-9_]*' by
+// replacing nonconforming characters with underscores.
+//
+// See frameworks/base/core/java/android/content/pm/PackageParser.java which
+// checks this at runtime.
 static std::string MakePackageSafeName(const std::string &name) {
   std::string result(name);
+  bool first = true;
   for (char &c : result) {
-    if (c == '-') {
-      c = '_';
+    if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+      first = false;
+      continue;
     }
+    if (!first) {
+      if (c >= '0' && c <= '9') {
+        continue;
+      }
+    }
+
+    c = '_';
+    first = false;
   }
   return result;
 }
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 9c33135..0c527f6 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -24,15 +24,16 @@
 
 TEST(UtilTest, SplitNamesAreSanitized) {
     AppInfo app_info{"com.pkg"};
-    SplitConstraints split_constraints{{test::ParseConfigOrDie("en-rUS-land")}};
+    SplitConstraints split_constraints{
+        {test::ParseConfigOrDie("en-rUS-land"), test::ParseConfigOrDie("b+sr+Latn")}};
 
     const auto doc = GenerateSplitManifest(app_info, split_constraints);
     const auto &root = doc->root;
     EXPECT_EQ(root->name, "manifest");
-    // split names cannot contain hyphens
-    EXPECT_EQ(root->FindAttribute("", "split")->value, "config.en_rUS_land");
+    // split names cannot contain hyphens or plus signs.
+    EXPECT_EQ(root->FindAttribute("", "split")->value, "config.b_sr_Latn_en_rUS_land");
     // but we should use resource qualifiers verbatim in 'targetConfig'.
-    EXPECT_EQ(root->FindAttribute("", "targetConfig")->value, "en-rUS-land");
+    EXPECT_EQ(root->FindAttribute("", "targetConfig")->value, "b+sr+Latn,en-rUS-land");
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 4a1b46c..2a51df3 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -23,6 +23,7 @@
 
 #include "android-base/logging.h"
 #include "android-base/macros.h"
+#include "android-base/stringprintf.h"
 
 #include "ResourceTable.h"
 #include "ResourceValues.h"
@@ -579,7 +580,15 @@
       // Write a self mapping entry for this package if the ID is non-standard (0x7f).
       const uint8_t package_id = package->id.value();
       if (package_id != kFrameworkPackageId && package_id != kAppPackageId) {
-        table->included_packages_[package_id] = package->name;
+        auto result = table->included_packages_.insert({package_id, package->name});
+        if (!result.second && result.first->second != package->name) {
+          // A mapping for this package ID already exists, and is a different package. Error!
+          context->GetDiagnostics()->Error(
+              DiagMessage() << android::base::StringPrintf(
+                  "can't map package ID %02x to '%s'. Already mapped to '%s'", package_id,
+                  package->name.c_str(), result.first->second.c_str()));
+          return false;
+        }
       }
     }
 
diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp
index 7ee1016..9751632 100644
--- a/tools/aapt2/io/Util.cpp
+++ b/tools/aapt2/io/Util.cpp
@@ -48,6 +48,12 @@
   return CopyInputStreamToArchive(context, data.get(), out_path, compression_flags, writer);
 }
 
+bool CopyFileToArchivePreserveCompression(IAaptContext* context, io::IFile* file,
+                                          const std::string& out_path, IArchiveWriter* writer) {
+  uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
+  return CopyFileToArchive(context, file, out_path, compression_flags, writer);
+}
+
 bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg,
                         const std::string& out_path, uint32_t compression_flags,
                         IArchiveWriter* writer) {
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index de2ab39..b07fb53 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -35,6 +35,9 @@
 bool CopyFileToArchive(IAaptContext* context, IFile* file, const std::string& out_path,
                        uint32_t compression_flags, IArchiveWriter* writer);
 
+bool CopyFileToArchivePreserveCompression(IAaptContext* context, IFile* file,
+                                          const std::string& out_path, IArchiveWriter* writer);
+
 bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg,
                         const std::string& out_path, uint32_t compression_flags,
                         IArchiveWriter* writer);
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index b9ae654..b214d21 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -345,15 +345,14 @@
       *out << "# Referenced at " << location.source << "\n";
     }
     if (keep_set.conditional_keep_rules_ && can_be_conditional) {
-      *out << "-keep class " << entry.first << " {\n  ifused class **.R$layout {\n";
+      *out << "-if class **.R$layout {\n";
       for (const UsageLocation& location : locations) {
         auto transformed_name = JavaClassGenerator::TransformToFieldName(location.name.entry);
-        *out << "    int " << transformed_name << ";\n";
+        *out << "  int " << transformed_name << ";\n";
       }
-      *out << "  };\n  <init>(...);\n}\n" << std::endl;
-    } else {
-      *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+      *out << "}\n";
     }
+    *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
   }
 
   for (const auto& entry : keep_set.method_set_) {
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index df3ac8b..802c56a 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -130,7 +130,7 @@
   ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
 
   std::string actual = out.str();
-  EXPECT_THAT(actual, HasSubstr("ifused class **.R$layout"));
+  EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
   EXPECT_THAT(actual, HasSubstr("int foo"));
   EXPECT_THAT(actual, HasSubstr("int bar"));
   EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -152,7 +152,7 @@
   ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
 
   std::string actual = out.str();
-  EXPECT_THAT(actual, HasSubstr("ifused class **.R$layout"));
+  EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
   EXPECT_THAT(actual, HasSubstr("int foo"));
   EXPECT_THAT(actual, HasSubstr("int bar"));
   EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -174,7 +174,7 @@
   ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
 
   std::string actual = out.str();
-  EXPECT_THAT(actual, Not(HasSubstr("ifused")));
+  EXPECT_THAT(actual, Not(HasSubstr("-if")));
 }
 
 TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 6fb1793..de4fb73 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -127,9 +127,9 @@
     diag->Error(DiagMessage(el->line_number)
                 << "attribute 'package' in <manifest> tag must not be a reference");
     return false;
-  } else if (!util::IsJavaPackageName(attr->value)) {
+  } else if (!util::IsAndroidPackageName(attr->value)) {
     diag->Error(DiagMessage(el->line_number)
-                << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
+                << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
                 << attr->value << "'");
     return false;
   }
diff --git a/tools/aapt2/text/Unicode.cpp b/tools/aapt2/text/Unicode.cpp
index 75eeb46..3735b3e 100644
--- a/tools/aapt2/text/Unicode.cpp
+++ b/tools/aapt2/text/Unicode.cpp
@@ -85,7 +85,8 @@
     return false;
   }
 
-  if (!IsXidStart(iter.Next())) {
+  const char32_t first_codepoint = iter.Next();
+  if (!IsXidStart(first_codepoint) && first_codepoint != U'_' && first_codepoint != U'$') {
     return false;
   }
 
diff --git a/tools/aapt2/text/Unicode_test.cpp b/tools/aapt2/text/Unicode_test.cpp
index d47fb28..a8e797c 100644
--- a/tools/aapt2/text/Unicode_test.cpp
+++ b/tools/aapt2/text/Unicode_test.cpp
@@ -44,10 +44,11 @@
 TEST(UnicodeTest, IsJavaIdentifier) {
   EXPECT_TRUE(IsJavaIdentifier("FøøBar_12"));
   EXPECT_TRUE(IsJavaIdentifier("Føø$Bar"));
+  EXPECT_TRUE(IsJavaIdentifier("_FøøBar"));
+  EXPECT_TRUE(IsJavaIdentifier("$Føø$Bar"));
 
   EXPECT_FALSE(IsJavaIdentifier("12FøøBar"));
-  EXPECT_FALSE(IsJavaIdentifier("_FøøBar"));
-  EXPECT_FALSE(IsJavaIdentifier("$Føø$Bar"));
+  EXPECT_FALSE(IsJavaIdentifier(".Hello"));
 }
 
 TEST(UnicodeTest, IsValidResourceEntryName) {
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index a9b49d9..e42145d 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -24,6 +24,7 @@
 #include "androidfw/StringPiece.h"
 #include "utils/Unicode.h"
 
+#include "text/Unicode.h"
 #include "text/Utf8Iterator.h"
 #include "util/BigBuffer.h"
 #include "util/Maybe.h"
@@ -94,72 +95,55 @@
   return StringPiece(start, end - start);
 }
 
-StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
-    const StringPiece& str, const StringPiece& allowed_chars) {
-  const auto end_iter = str.end();
-  for (auto iter = str.begin(); iter != end_iter; ++iter) {
-    char c = *iter;
-    if ((c >= u'a' && c <= u'z') || (c >= u'A' && c <= u'Z') ||
-        (c >= u'0' && c <= u'9')) {
-      continue;
-    }
-
-    bool match = false;
-    for (char i : allowed_chars) {
-      if (c == i) {
-        match = true;
-        break;
-      }
-    }
-
-    if (!match) {
-      return iter;
+static int IsJavaNameImpl(const StringPiece& str) {
+  int pieces = 0;
+  for (const StringPiece& piece : Tokenize(str, '.')) {
+    pieces++;
+    if (!text::IsJavaIdentifier(piece)) {
+      return -1;
     }
   }
-  return end_iter;
+  return pieces;
 }
 
 bool IsJavaClassName(const StringPiece& str) {
-  size_t pieces = 0;
-  for (const StringPiece& piece : Tokenize(str, '.')) {
-    pieces++;
-    if (piece.empty()) {
-      return false;
-    }
-
-    // Can't have starting or trailing $ character.
-    if (piece.data()[0] == '$' || piece.data()[piece.size() - 1] == '$') {
-      return false;
-    }
-
-    if (FindNonAlphaNumericAndNotInSet(piece, "$_") != piece.end()) {
-      return false;
-    }
-  }
-  return pieces >= 2;
+  return IsJavaNameImpl(str) >= 2;
 }
 
 bool IsJavaPackageName(const StringPiece& str) {
-  if (str.empty()) {
-    return false;
-  }
+  return IsJavaNameImpl(str) >= 1;
+}
 
-  size_t pieces = 0;
+static int IsAndroidNameImpl(const StringPiece& str) {
+  int pieces = 0;
   for (const StringPiece& piece : Tokenize(str, '.')) {
-    pieces++;
     if (piece.empty()) {
-      return false;
+      return -1;
     }
 
-    if (piece.data()[0] == '_' || piece.data()[piece.size() - 1] == '_') {
-      return false;
+    const char first_character = piece.data()[0];
+    if (!::isalpha(first_character)) {
+      return -1;
     }
 
-    if (FindNonAlphaNumericAndNotInSet(piece, "_") != piece.end()) {
-      return false;
+    bool valid = std::all_of(piece.begin() + 1, piece.end(), [](const char c) -> bool {
+      return ::isalnum(c) || c == '_';
+    });
+
+    if (!valid) {
+      return -1;
     }
+    pieces++;
   }
-  return pieces >= 1;
+  return pieces;
+}
+
+bool IsAndroidPackageName(const StringPiece& str) {
+  return IsAndroidNameImpl(str) > 1 || str == "android";
+}
+
+bool IsAndroidSplitName(const StringPiece& str) {
+  return IsAndroidNameImpl(str) > 0;
 }
 
 Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
@@ -176,7 +160,7 @@
     return {};
   }
 
-  std::string result(package.data(), package.size());
+  std::string result = package.to_string();
   if (classname.data()[0] != '.') {
     result += '.';
   }
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index c928458..7c949b90 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -53,48 +53,40 @@
 std::vector<std::string> Split(const android::StringPiece& str, char sep);
 std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
 
-/**
- * Returns true if the string starts with prefix.
- */
+// Returns true if the string starts with prefix.
 bool StartsWith(const android::StringPiece& str, const android::StringPiece& prefix);
 
-/**
- * Returns true if the string ends with suffix.
- */
+// Returns true if the string ends with suffix.
 bool EndsWith(const android::StringPiece& str, const android::StringPiece& suffix);
 
-/**
- * Creates a new StringPiece16 that points to a substring
- * of the original string without leading or trailing whitespace.
- */
+// Creates a new StringPiece16 that points to a substring of the original string without leading or
+// trailing whitespace.
 android::StringPiece TrimWhitespace(const android::StringPiece& str);
 
-/**
- * Returns an iterator to the first character that is not alpha-numeric and that
- * is not in the allowedChars set.
- */
-android::StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
-    const android::StringPiece& str, const android::StringPiece& allowed_chars);
-
-/**
- * Tests that the string is a valid Java class name.
- */
+// Tests that the string is a valid Java class name.
 bool IsJavaClassName(const android::StringPiece& str);
 
-/**
- * Tests that the string is a valid Java package name.
- */
+// Tests that the string is a valid Java package name.
 bool IsJavaPackageName(const android::StringPiece& str);
 
-/**
- * Converts the class name to a fully qualified class name from the given
- * `package`. Ex:
- *
- * asdf         --> package.asdf
- * .asdf        --> package.asdf
- * .a.b         --> package.a.b
- * asdf.adsf    --> asdf.adsf
- */
+// Tests that the string is a valid Android package name. More strict than a Java package name.
+// - First character of each component (separated by '.') must be an ASCII letter.
+// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
+// - Package must contain at least two components, unless it is 'android'.
+bool IsAndroidPackageName(const android::StringPiece& str);
+
+// Tests that the string is a valid Android split name.
+// - First character of each component (separated by '.') must be an ASCII letter.
+// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
+bool IsAndroidSplitName(const android::StringPiece& str);
+
+// Converts the class name to a fully qualified class name from the given
+// `package`. Ex:
+//
+// asdf         --> package.asdf
+// .asdf        --> package.asdf
+// .a.b         --> package.a.b
+// asdf.adsf    --> asdf.adsf
 Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
                                               const android::StringPiece& class_name);
 
@@ -108,23 +100,17 @@
   return 0;
 }
 
-/**
- * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
- * This will be present in C++14 and can be removed then.
- */
+// Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
+// This will be present in C++14 and can be removed then.
 template <typename T, class... Args>
 std::unique_ptr<T> make_unique(Args&&... args) {
   return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
 }
 
-/**
- * Writes a set of items to the std::ostream, joining the times with the
- * provided
- * separator.
- */
+// Writes a set of items to the std::ostream, joining the times with the provided separator.
 template <typename Container>
-::std::function<::std::ostream&(::std::ostream&)> Joiner(
-    const Container& container, const char* sep) {
+::std::function<::std::ostream&(::std::ostream&)> Joiner(const Container& container,
+                                                         const char* sep) {
   using std::begin;
   using std::end;
   const auto begin_iter = begin(container);
@@ -140,32 +126,19 @@
   };
 }
 
-/**
- * Helper method to extract a UTF-16 string from a StringPool. If the string is
- * stored as UTF-8,
- * the conversion to UTF-16 happens within ResStringPool.
- */
+// Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
+// the conversion to UTF-16 happens within ResStringPool.
 android::StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx);
 
-/**
- * Helper method to extract a UTF-8 string from a StringPool. If the string is
- * stored as UTF-16,
- * the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is
- * done by this method,
- * which maintains no state or cache. This means we must return an std::string
- * copy.
- */
+// Helper method to extract a UTF-8 string from a StringPool. If the string is stored as UTF-16,
+// the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is done by this method,
+// which maintains no state or cache. This means we must return an std::string copy.
 std::string GetString(const android::ResStringPool& pool, size_t idx);
 
-/**
- * Checks that the Java string format contains no non-positional arguments
- * (arguments without
- * explicitly specifying an index) when there are more than one argument. This
- * is an error
- * because translations may rearrange the order of the arguments in the string,
- * which will
- * break the string interpolation.
- */
+// Checks that the Java string format contains no non-positional arguments (arguments without
+// explicitly specifying an index) when there are more than one argument. This is an error
+// because translations may rearrange the order of the arguments in the string, which will
+// break the string interpolation.
 bool VerifyJavaStringFormat(const android::StringPiece& str);
 
 class StringBuilder {
@@ -194,36 +167,38 @@
   std::string error_;
 };
 
-inline const std::string& StringBuilder::ToString() const { return str_; }
+inline const std::string& StringBuilder::ToString() const {
+  return str_;
+}
 
-inline const std::string& StringBuilder::Error() const { return error_; }
+inline const std::string& StringBuilder::Error() const {
+  return error_;
+}
 
-inline bool StringBuilder::IsEmpty() const { return str_.empty(); }
+inline bool StringBuilder::IsEmpty() const {
+  return str_.empty();
+}
 
-inline size_t StringBuilder::Utf16Len() const { return utf16_len_; }
+inline size_t StringBuilder::Utf16Len() const {
+  return utf16_len_;
+}
 
-inline StringBuilder::operator bool() const { return error_.empty(); }
+inline StringBuilder::operator bool() const {
+  return error_.empty();
+}
 
-/**
- * Converts a UTF8 string to a UTF16 string.
- */
+// Converts a UTF8 string to a UTF16 string.
 std::u16string Utf8ToUtf16(const android::StringPiece& utf8);
 std::string Utf16ToUtf8(const android::StringPiece16& utf16);
 
-/**
- * Writes the entire BigBuffer to the output stream.
- */
+// Writes the entire BigBuffer to the output stream.
 bool WriteAll(std::ostream& out, const BigBuffer& buffer);
 
-/*
- * Copies the entire BigBuffer into a single buffer.
- */
+// Copies the entire BigBuffer into a single buffer.
 std::unique_ptr<uint8_t[]> Copy(const BigBuffer& buffer);
 
-/**
- * A Tokenizer implemented as an iterable collection. It does not allocate
- * any memory on the heap nor use standard containers.
- */
+// A Tokenizer implemented as an iterable collection. It does not allocate any memory on the heap
+// nor use standard containers.
 class Tokenizer {
  public:
   class iterator {
@@ -269,38 +244,42 @@
   const iterator end_;
 };
 
-inline Tokenizer Tokenize(const android::StringPiece& str, char sep) { return Tokenizer(str, sep); }
+inline Tokenizer Tokenize(const android::StringPiece& str, char sep) {
+  return Tokenizer(str, sep);
+}
 
-inline uint16_t HostToDevice16(uint16_t value) { return htods(value); }
+inline uint16_t HostToDevice16(uint16_t value) {
+  return htods(value);
+}
 
-inline uint32_t HostToDevice32(uint32_t value) { return htodl(value); }
+inline uint32_t HostToDevice32(uint32_t value) {
+  return htodl(value);
+}
 
-inline uint16_t DeviceToHost16(uint16_t value) { return dtohs(value); }
+inline uint16_t DeviceToHost16(uint16_t value) {
+  return dtohs(value);
+}
 
-inline uint32_t DeviceToHost32(uint32_t value) { return dtohl(value); }
+inline uint32_t DeviceToHost32(uint32_t value) {
+  return dtohl(value);
+}
 
-/**
- * Given a path like: res/xml-sw600dp/foo.xml
- *
- * Extracts "res/xml-sw600dp/" into outPrefix.
- * Extracts "foo" into outEntry.
- * Extracts ".xml" into outSuffix.
- *
- * Returns true if successful.
- */
+// Given a path like: res/xml-sw600dp/foo.xml
+//
+// Extracts "res/xml-sw600dp/" into outPrefix.
+// Extracts "foo" into outEntry.
+// Extracts ".xml" into outSuffix.
+//
+// Returns true if successful.
 bool ExtractResFilePathParts(const android::StringPiece& path, android::StringPiece* out_prefix,
                              android::StringPiece* out_entry, android::StringPiece* out_suffix);
 
 }  // namespace util
 
-/**
- * Stream operator for functions. Calls the function with the stream as an
- * argument.
- * In the aapt namespace for lookup.
- */
-inline ::std::ostream& operator<<(
-    ::std::ostream& out,
-    const ::std::function<::std::ostream&(::std::ostream&)>& f) {
+// Stream operator for functions. Calls the function with the stream as an argument.
+// In the aapt namespace for lookup.
+inline ::std::ostream& operator<<(::std::ostream& out,
+                                  const ::std::function<::std::ostream&(::std::ostream&)>& f) {
   return f(out);
 }
 
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index adb5291..2d1242a 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -117,24 +117,46 @@
   EXPECT_TRUE(util::IsJavaClassName("android.test.Class$Inner"));
   EXPECT_TRUE(util::IsJavaClassName("android_test.test.Class"));
   EXPECT_TRUE(util::IsJavaClassName("_android_.test._Class_"));
-  EXPECT_FALSE(util::IsJavaClassName("android.test.$Inner"));
-  EXPECT_FALSE(util::IsJavaClassName("android.test.Inner$"));
+  EXPECT_TRUE(util::IsJavaClassName("android.test.$Inner"));
+  EXPECT_TRUE(util::IsJavaClassName("android.test.Inner$"));
+  EXPECT_TRUE(util::IsJavaClassName("com.foo.FøøBar"));
+
   EXPECT_FALSE(util::IsJavaClassName(".test.Class"));
   EXPECT_FALSE(util::IsJavaClassName("android"));
+  EXPECT_FALSE(util::IsJavaClassName("FooBar"));
 }
 
 TEST(UtilTest, IsJavaPackageName) {
   EXPECT_TRUE(util::IsJavaPackageName("android"));
   EXPECT_TRUE(util::IsJavaPackageName("android.test"));
   EXPECT_TRUE(util::IsJavaPackageName("android.test_thing"));
-  EXPECT_FALSE(util::IsJavaPackageName("_android"));
-  EXPECT_FALSE(util::IsJavaPackageName("android_"));
+  EXPECT_TRUE(util::IsJavaPackageName("_android"));
+  EXPECT_TRUE(util::IsJavaPackageName("android_"));
+  EXPECT_TRUE(util::IsJavaPackageName("android._test"));
+  EXPECT_TRUE(util::IsJavaPackageName("cøm.foo"));
+
   EXPECT_FALSE(util::IsJavaPackageName("android."));
   EXPECT_FALSE(util::IsJavaPackageName(".android"));
-  EXPECT_FALSE(util::IsJavaPackageName("android._test"));
   EXPECT_FALSE(util::IsJavaPackageName(".."));
 }
 
+TEST(UtilTest, IsAndroidPackageName) {
+  EXPECT_TRUE(util::IsAndroidPackageName("android"));
+  EXPECT_TRUE(util::IsAndroidPackageName("android.test"));
+  EXPECT_TRUE(util::IsAndroidPackageName("com.foo"));
+  EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_thing"));
+  EXPECT_TRUE(util::IsAndroidPackageName("com.foo.testing_thing_"));
+  EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_99_"));
+
+  EXPECT_FALSE(util::IsAndroidPackageName("android._test"));
+  EXPECT_FALSE(util::IsAndroidPackageName("com"));
+  EXPECT_FALSE(util::IsAndroidPackageName("_android"));
+  EXPECT_FALSE(util::IsAndroidPackageName("android."));
+  EXPECT_FALSE(util::IsAndroidPackageName(".android"));
+  EXPECT_FALSE(util::IsAndroidPackageName(".."));
+  EXPECT_FALSE(util::IsAndroidPackageName("cøm.foo"));
+}
+
 TEST(UtilTest, FullyQualifiedClassName) {
   EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".asdf"), Eq("android.asdf"));
   EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".a.b"), Eq("android.a.b"));
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 2eab22e..c9f3199 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1371,7 +1371,9 @@
             print
         """
 
-    print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
-    for f in sorted(cur_fail):
-        print cur_fail[f]
-        print
+    if len(cur_fail) != 0:
+        print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+        for f in sorted(cur_fail):
+            print cur_fail[f]
+            print
+        sys.exit(77)
diff --git a/tools/apilint/apilint_sha.sh b/tools/apilint/apilint_sha.sh
new file mode 100755
index 0000000..2a45b10
--- /dev/null
+++ b/tools/apilint/apilint_sha.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+    python tools/apilint/apilint.py <(git show $1:api/current.txt) <(git show $1^:api/current.txt)
+fi
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index 4816984..9aef562 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -18,6 +18,12 @@
     return file_descriptor.name() + ".h";
 }
 
+static inline bool
+should_generate_enums_mapping(const EnumDescriptorProto& enu)
+{
+    return enu.options().GetExtension(stream_enum).enable_enums_mapping();
+}
+
 static void
 write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
 {
@@ -29,6 +35,23 @@
                 << make_constant_name(value.name())
                 << " = " << value.number() << ";" << endl;
     }
+
+    if (should_generate_enums_mapping(enu)) {
+        string name = make_constant_name(enu.name());
+        string prefix = name + "_";
+        text << indent << "const int _ENUM_" << name << "_COUNT = " << N << ";" << endl;
+        text << indent << "const char* _ENUM_" << name << "_NAMES[" << N << "] = {" << endl;
+        for (int i=0; i<N; i++) {
+            text << indent << INDENT << "\"" << stripPrefix(enu.value(i).name(), prefix) << "\"," << endl;
+        }
+        text << indent << "};" << endl;
+        text << indent << "const uint32_t _ENUM_" << name << "_VALUES[" << N << "] = {" << endl;
+        for (int i=0; i<N; i++) {
+            text << indent << INDENT << make_constant_name(enu.value(i).name()) << "," << endl;
+        }
+        text << indent << "};" << endl;
+    }
+
     text << endl;
 }
 
@@ -59,7 +82,7 @@
 static inline bool
 should_generate_fields_mapping(const DescriptorProto& message)
 {
-    return message.options().GetExtension(stream).enable_fields_mapping();
+    return message.options().GetExtension(stream_msg).enable_fields_mapping();
 }
 
 static void
diff --git a/tools/streaming_proto/stream.proto b/tools/streaming_proto/stream.proto
index 123506c..c081209 100644
--- a/tools/streaming_proto/stream.proto
+++ b/tools/streaming_proto/stream.proto
@@ -21,12 +21,22 @@
 package android.stream_proto;
 
 // This option tells streaming proto plugin to compile .proto files with extra features.
-message StreamFlags {
+message MessageOptions {
   // creates a mapping of field names of the message to its field ids
   optional bool enable_fields_mapping = 1;
 }
 
 extend google.protobuf.MessageOptions {
     // Flags used by streaming proto plugins
-    optional StreamFlags stream = 126856794;
+    optional MessageOptions stream_msg = 126856794;
+}
+
+message EnumOptions {
+  // creates a mapping of enum names to its values, strip its prefix enum type for each value
+  optional bool enable_enums_mapping = 1;
+}
+
+extend google.protobuf.EnumOptions {
+    // Flags used by streaming proto plugins
+    optional EnumOptions stream_enum = 126856794;
 }
diff --git a/tools/streaming_proto/string_utils.cpp b/tools/streaming_proto/string_utils.cpp
index bd34ab7..607d820 100644
--- a/tools/streaming_proto/string_utils.cpp
+++ b/tools/streaming_proto/string_utils.cpp
@@ -108,6 +108,17 @@
     return result;
 }
 
+string
+stripPrefix(const string& str, const string& prefix)
+{
+    if (str.size() <= prefix.size()) return str;
+    size_t i = 0, len = prefix.size();
+    for (; i<len; i++) {
+        if (str[i] != prefix[i]) return str;
+    }
+    return str.substr(i);
+}
+
 } // namespace stream_proto
 } // namespace android
 
diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h
index d6f195f..315b275 100644
--- a/tools/streaming_proto/string_utils.h
+++ b/tools/streaming_proto/string_utils.h
@@ -26,15 +26,20 @@
 string file_base_name(const string& str);
 
 /**
- * Replace all occurances of 'replace' with 'with'.
+ * Replaces all occurances of 'replace' with 'with'.
  */
 string replace_string(const string& str, const char replace, const char with);
 
 /**
- * Split a string to parts by delimiter.
+ * Splits a string to parts by delimiter.
  */
 vector<string> split(const string& str, const char delimiter);
 
+/**
+ * Returns the rest of str if it has prefix, otherwise return all.
+ */
+string stripPrefix(const string& str, const string& prefix);
+
 } // namespace stream_proto
 } // namespace android
 
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index a367b23..bf8fed1 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -348,6 +348,9 @@
      * quotation marks. Otherwise, it is returned as a string of hex digits. The
      * SSID may be &lt;unknown ssid&gt; if there is no network currently connected,
      * or if the caller has insufficient permissions to access the SSID.
+     *
+     * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
+     * always returned the SSID with no quotes around it.
      * @return the SSID
      */
     public String getSSID() {