Merge "Listen for launcher package added to connect to it faster"
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 817aa80..b847425 100644
--- a/Android.mk
+++ b/Android.mk
@@ -159,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 \
@@ -1557,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 c63ddbf..099a46f 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,7 +4943,6 @@
     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();
@@ -5049,7 +5048,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();
@@ -5063,7 +5062,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[]);
@@ -5073,7 +5072,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>);
@@ -8533,7 +8532,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();
@@ -9106,6 +9105,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";
@@ -9272,7 +9272,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);
@@ -9878,7 +9878,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();
@@ -9911,15 +9911,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);
   }
 
@@ -11227,6 +11227,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 {
@@ -32608,7 +32617,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);
@@ -32619,7 +32628,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);
   }
 
@@ -39326,7 +39335,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);
@@ -39458,6 +39469,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);
@@ -39648,6 +39660,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";
@@ -49970,7 +49983,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 119040e..3a5abd1 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,7 +5129,6 @@
     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();
@@ -5235,7 +5234,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();
@@ -5249,7 +5248,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[]);
@@ -5259,7 +5258,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>);
@@ -9045,7 +9044,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();
@@ -9627,6 +9626,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";
@@ -9806,7 +9806,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);
@@ -10441,7 +10441,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();
@@ -10474,15 +10474,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);
   }
 
@@ -11960,6 +11960,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 {
@@ -34819,11 +34828,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);
@@ -35492,7 +35503,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);
@@ -35503,7 +35514,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);
   }
 
@@ -42652,7 +42663,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);
@@ -42791,6 +42804,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);
@@ -42984,6 +42998,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";
@@ -43122,6 +43137,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);
   }
@@ -53895,7 +53911,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 8062d16..261f173 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,7 +4972,6 @@
     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();
@@ -5078,7 +5077,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();
@@ -5092,7 +5091,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[]);
@@ -5102,7 +5101,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>);
@@ -8608,7 +8607,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();
@@ -9183,6 +9182,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";
@@ -9350,7 +9350,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);
@@ -9956,7 +9956,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();
@@ -9989,15 +9989,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);
   }
 
@@ -11316,6 +11316,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 {
@@ -32877,7 +32886,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);
@@ -32888,7 +32897,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);
   }
 
@@ -39717,7 +39726,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);
@@ -39852,6 +39863,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);
@@ -40055,6 +40067,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";
@@ -50618,7 +50631,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 3cef6b3..5740b33 100644
--- a/cmds/incident_helper/tests/ih_util_test.cpp
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -62,6 +62,59 @@
     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);
@@ -79,23 +132,6 @@
     ASSERT_TRUE(r.ok(&line));
 }
 
-TEST(IhUtilTest, ReaderSmallBufSize) {
-    TemporaryFile tf;
-    ASSERT_NE(tf.fd, -1);
-    ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooiecccojreo", tf.path));
-
-    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);
@@ -103,9 +139,8 @@
 
     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));
 }
 
@@ -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/statsd/Android.mk b/cmds/statsd/Android.mk
index 929c3cc..0f6d868 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -31,7 +31,9 @@
     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 \
@@ -162,7 +164,8 @@
     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 7ff42b6..8c70bb5 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -25,6 +25,7 @@
 #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;
@@ -46,7 +47,7 @@
 const int FIELD_ID_UID_MAP = 3;
 // for ConfigKey
 const int FIELD_ID_UID = 1;
-const int FIELD_ID_NAME = 1;
+const int FIELD_ID_NAME = 2;
 
 StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
                                      const std::function<void(const vector<uint8_t>&)>& pushLog)
@@ -119,8 +120,8 @@
     // 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_ID_METRICS, reinterpret_cast<char*>(m.get()->data()),
-                    m.get()->size());
+        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS,
+                    reinterpret_cast<char*>(m.get()->data()), m.get()->size());
     }
 
     // Fill in UidMap.
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/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/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 0c9e522..2252201 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -24,6 +24,7 @@
 #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;
@@ -134,11 +135,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());
@@ -154,7 +157,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,
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 2783bce..74ce9cd 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -23,6 +23,7 @@
 #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;
@@ -176,11 +177,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 +199,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,
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index f516cc7..a65092f 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -23,6 +23,7 @@
 #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;
@@ -94,6 +95,7 @@
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
 
     startNewProtoOutputStream(endTime);
+    mByteSize = 0;
 
     return buffer;
 }
@@ -111,16 +113,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 0dccdf4..7740621 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -65,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 285d29b..fafeee4 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -24,6 +24,7 @@
 #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;
@@ -123,11 +124,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 +146,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,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index d80672d..f9e4deb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -81,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 3c4dd90..62fb632 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -73,7 +73,7 @@
     auto it = mProto->data();
     while (it.readBuffer() != NULL) {
         size_t toRead = it.currentToRead();
-        std::memcpy(&buffer.get()[pos], it.readBuffer(), toRead);
+        std::memcpy(&((*buffer)[pos]), it.readBuffer(), toRead);
         pos += toRead;
         it.rp()->move(toRead);
     }
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 2d9f369..e8a862f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -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) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 2a63073..5cffec1 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -23,6 +23,7 @@
 #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;
@@ -30,6 +31,7 @@
 using android::util::FIELD_TYPE_MESSAGE;
 using android::util::ProtoOutputStream;
 using std::list;
+using std::make_pair;
 using std::make_shared;
 using std::map;
 using std::shared_ptr;
@@ -61,13 +63,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,8 +90,9 @@
     }
 
     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);
@@ -88,8 +101,19 @@
          (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) {
@@ -119,11 +143,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 +165,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,
@@ -168,22 +195,22 @@
 }
 
 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);
             }
@@ -194,12 +221,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);
         }
@@ -222,24 +253,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));
     }
 }
 
@@ -249,7 +292,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;
     }
 }
@@ -267,13 +310,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);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index ef9868b..c6c87f5 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <gtest/gtest_prod.h>
 #include <utils/threads.h>
 #include <list>
 #include "../condition/ConditionTracker.h"
@@ -71,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;
 
@@ -81,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;
@@ -97,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/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index ca9cdfb..226e4d1 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.
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 7b27642..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);
@@ -202,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/stats_events.proto b/cmds/statsd/src/stats_events.proto
index 8816795..9ab07de 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -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;
     }
 }
 
@@ -849,6 +852,7 @@
 }
 
 /**
+<<<<<<< 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.
@@ -866,3 +870,42 @@
     // 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/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
new file mode 100644
index 0000000..2a26388
--- /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_metric_id(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_metric_id(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_metric_id(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/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 d49e11f..e260967 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import android.annotation.IntDef;
-import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -419,51 +418,22 @@
      * 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) {
@@ -477,7 +447,7 @@
                         + myProc + " resolved to different process "
                         + ai.processName + ": " + intent);
             }
-
+    
             intent.setComponent(new ComponentName(
                     ai.applicationInfo.packageName, ai.name));
             final ActivityWaiter aw = new ActivityWaiter(intent);
@@ -487,7 +457,7 @@
             }
             mWaitingActivities.add(aw);
 
-            getTargetContext().startActivity(intent, options);
+            getTargetContext().startActivity(intent);
 
             do {
                 try {
@@ -495,7 +465,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 4efc2c7..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;
@@ -922,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/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 23956e1..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);
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/Context.java b/core/java/android/content/Context.java
index 01ad3ad..72f75112 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4073,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/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/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/UserManager.java b/core/java/android/os/UserManager.java
index de52736..22967af 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1049,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) {
@@ -1075,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
@@ -1094,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.
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/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/view/DisplayFrames.java b/core/java/android/view/DisplayFrames.java
deleted file mode 100644
index e6861d8..0000000
--- a/core/java/android/view/DisplayFrames.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.view;
-
-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/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 534335b..ebe3633 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -66,6 +66,7 @@
 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;
@@ -721,6 +722,12 @@
     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.
@@ -1166,10 +1173,14 @@
     /**
      * Called when layout of the windows is about to start.
      *
-     * @param displayFrames frames of the display we are doing layout on.
+     * @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 uiMode The current uiMode in configuration.
      */
-    default void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {}
+    public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
+                              int displayRotation, int uiMode);
 
     /**
      * Returns the bottom-most layer of the system decor, above which no policy decor should
@@ -1178,28 +1189,37 @@
     public int getSystemDecorLayerLw();
 
     /**
-     * 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().
+     * 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().
      *
      * @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.
      */
-    default void layoutWindowLw(
-            WindowState win, WindowState attached, DisplayFrames displayFrames) {}
+    public void layoutWindowLw(WindowState win, WindowState attached);
 
 
     /**
-     * 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 displayFrames display frames.
+     * @param displayRotation Rotation of the display.
+     * @param displayWidth The width of the display.
+     * @param displayHeight The height of the display.
      * @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.
@@ -1207,11 +1227,16 @@
      * @return Whether to always consume the navigation bar.
      *         See {@link #isNavBarForcedShownLw(WindowState)}.
      */
-    default boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
-            DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets) {
-        return false;
-    }
+    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();
 
     /** Layout state may have changed (so another layout will be performed) */
     static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001;
@@ -1628,6 +1653,11 @@
     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/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 107013a..2ad6e02 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -235,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;
             }
@@ -256,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;
             }
@@ -303,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;
         }
     }
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/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/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 18afe6e..ac79249 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -137,15 +137,18 @@
 
 class StyleRun : public Run {
     public:
-        StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint, bool isRtl)
-            : Run(start, end), mPaint(std::move(paint)), mIsRtl(isRtl) {}
+        StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
+                std::shared_ptr<minikin::FontCollection>&& collection, bool isRtl)
+            : Run(start, end), mPaint(std::move(paint)), mCollection(std::move(collection)),
+              mIsRtl(isRtl) {}
 
         void addTo(minikin::LineBreaker* lineBreaker) override {
-            lineBreaker->addStyleRun(&mPaint, mStart, mEnd, mIsRtl);
+            lineBreaker->addStyleRun(&mPaint, mCollection, mStart, mEnd, mIsRtl);
         }
 
     private:
         minikin::MinikinPaint mPaint;
+        std::shared_ptr<minikin::FontCollection> mCollection;
         const bool mIsRtl;
 };
 
@@ -173,8 +176,10 @@
               mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
               mRightPaddings(std::move(rightPaddings)) {}
 
-        void addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint, bool isRtl) {
-            mRuns.emplace_back(std::make_unique<StyleRun>(start, end, std::move(paint), isRtl));
+        void addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
+                         std::shared_ptr<minikin::FontCollection> collection, bool isRtl) {
+            mRuns.emplace_back(std::make_unique<StyleRun>(
+                    start, end, std::move(paint), std::move(collection), isRtl));
         }
 
         void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
@@ -329,7 +334,7 @@
     Paint* paint = reinterpret_cast<Paint*>(nativePaint);
     const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
     minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
-    builder->addStyleRun(start, end, std::move(minikinPaint), isRtl);
+    builder->addStyleRun(start, end, std::move(minikinPaint), typeface->fFontCollection, isRtl);
 }
 
 // CriticalNative
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..55ea285 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,6 +21,7 @@
 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";
@@ -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 [
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/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 0228edb..4d48a42 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -48,6 +48,7 @@
 
 /* represents PhoneWindowManager */
 message WindowManagerPolicyProto {
+  optional .android.graphics.RectProto stable_bounds = 1;
 }
 
 /* represents AppTransition */
@@ -100,13 +101,8 @@
   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/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/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index aff942d..3dc928d 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -475,19 +475,6 @@
     }
 
     /**
-     * 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/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/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/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 2a1a1f3..90fe0b7 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -30,43 +30,46 @@
                                                         const Typeface* typeface) {
     const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
     minikin::FontStyle resolved = resolvedFace->fStyle;
+
     const minikin::FontVariant minikinVariant =
             (paint->getFontVariant() == minikin::FontVariant::ELEGANT)
                     ? minikin::FontVariant::ELEGANT
                     : minikin::FontVariant::COMPACT;
-    float textSize = paint->getTextSize();
-    if (!paint->isLinearText()) {
-        // If linear text is not specified, truncate the value.
-        textSize = trunc(textSize);
-    }
-    return minikin::MinikinPaint(
-            textSize,
-            paint->getTextScaleX(),
-            paint->getTextSkewX(),
-            paint->getLetterSpacing(),
-            paint->getWordSpacing(),
-            MinikinFontSkia::packPaintFlags(paint),
-            paint->getMinikinLocaleListId(),
-            minikin::FontStyle(minikinVariant, resolved.weight, resolved.slant),
-            minikin::HyphenEdit(paint->getHyphenEdit()),
-            paint->getFontFeatureSettings(),
-            resolvedFace->fFontCollection);
+
+    minikin::MinikinPaint minikinPaint;
+    /* Prepare minikin Paint */
+    minikinPaint.size =
+            paint->isLinearText() ? paint->getTextSize() : static_cast<int>(paint->getTextSize());
+    minikinPaint.scaleX = paint->getTextScaleX();
+    minikinPaint.skewX = paint->getTextSkewX();
+    minikinPaint.letterSpacing = paint->getLetterSpacing();
+    minikinPaint.wordSpacing = paint->getWordSpacing();
+    minikinPaint.paintFlags = MinikinFontSkia::packPaintFlags(paint);
+    minikinPaint.localeListId = paint->getMinikinLocaleListId();
+    minikinPaint.fontStyle = minikin::FontStyle(minikinVariant, resolved.weight, resolved.slant);
+    minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
+    minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
+    return minikinPaint;
 }
 
 minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFlags,
                                        const Typeface* typeface, const uint16_t* buf, size_t start,
                                        size_t count, size_t bufSize) {
+    minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
     minikin::Layout layout;
-    layout.doLayout(buf, start, count, bufSize, bidiFlags, prepareMinikinPaint(paint, typeface));
+    layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint,
+                    Typeface::resolveDefault(typeface)->fFontCollection);
     return layout;
 }
 
 float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
                                 const Typeface* typeface, const uint16_t* buf, size_t start,
                                 size_t count, size_t bufSize, float* advances) {
-    return minikin::Layout::measureText(
-            buf, start, count, bufSize, bidiFlags, prepareMinikinPaint(paint, typeface), advances,
-            nullptr /* extent */, nullptr /* overhangs */);
+    minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
+    const Typeface* resolvedTypeface = Typeface::resolveDefault(typeface);
+    return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint,
+                                        resolvedTypeface->fFontCollection, advances,
+                                        nullptr /* extent */, nullptr /* overhangs */);
 }
 
 bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
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/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 574bb02..3e2eeee 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -141,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/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/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/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/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/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/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 8044b07..efe049a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3066,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
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/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 8e93d19..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
@@ -4690,6 +4696,68 @@
     // 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/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/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/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/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/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 a11d282..7837940 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -59,8 +59,6 @@
 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;
@@ -68,7 +66,6 @@
 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;
@@ -126,8 +123,11 @@
 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,7 +210,6 @@
 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;
@@ -597,6 +596,47 @@
 
     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.
@@ -694,11 +734,18 @@
 
     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;
 
@@ -1014,7 +1061,7 @@
             View.NAVIGATION_BAR_UNHIDE,
             View.NAVIGATION_BAR_TRANSLUCENT,
             StatusBarManager.WINDOW_NAVIGATION_BAR,
-            FLAG_TRANSLUCENT_NAVIGATION,
+            WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
             View.NAVIGATION_BAR_TRANSPARENT);
 
     private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
@@ -2021,7 +2068,6 @@
         context.registerReceiver(mMultiuserReceiver, filter);
 
         // monitor for system gestures
-        // TODO(multi-display): Needs to be display specific.
         mSystemGestures = new SystemGesturesPointerEventListener(context,
                 new SystemGesturesPointerEventListener.Callbacks() {
                     @Override
@@ -2268,6 +2314,17 @@
         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;
@@ -4264,16 +4321,12 @@
     }
 
     @Override
-    // TODO: Should probably be moved into DisplayFrames.
     public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
-            DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets) {
+            int displayRotation, int displayWidth, int displayHeight, 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) {
@@ -4296,33 +4349,34 @@
             int availRight, availBottom;
             if (canHideNavigationBar() &&
                     (systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
-                availRight = displayFrames.mUnrestricted.right;
-                availBottom = displayFrames.mUnrestricted.bottom;
+                availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+                availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
             } else {
-                availRight = displayFrames.mRestricted.right;
-                availBottom = displayFrames.mRestricted.bottom;
+                availRight = mRestrictedScreenLeft + mRestrictedScreenWidth;
+                availBottom = mRestrictedScreenTop + mRestrictedScreenHeight;
             }
-            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(displayFrames.mStableFullscreen.left,
-                            displayFrames.mStableFullscreen.top,
-                            availRight - displayFrames.mStableFullscreen.right,
-                            availBottom - displayFrames.mStableFullscreen.bottom);
+                    outContentInsets.set(mStableFullscreenLeft, mStableFullscreenTop,
+                            availRight - mStableFullscreenRight,
+                            availBottom - mStableFullscreenBottom);
                 } else {
-                    outContentInsets.set(outStableInsets);
+                    outContentInsets.set(mStableLeft, mStableTop,
+                            availRight - mStableRight, availBottom - mStableBottom);
                 }
             } 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(displayFrames.mCurrent.left, displayFrames.mCurrent.top,
-                        availRight - displayFrames.mCurrent.right,
-                        availBottom - displayFrames.mCurrent.bottom);
+                outContentInsets.set(mCurLeft, mCurTop,
+                        availRight - mCurRight, availBottom - mCurBottom);
             }
 
+            outStableInsets.set(mStableLeft, mStableTop,
+                    availRight - mStableRight, availBottom - mStableBottom);
             if (taskBounds != null) {
                 calculateRelevantTaskInsets(taskBounds, outContentInsets,
                         displayWidth, displayHeight);
@@ -4359,11 +4413,68 @@
 
     /** {@inheritDoc} */
     @Override
-    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();
+    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;
         mDockLayer = 0x10000000;
         mStatusBarLayer = -1;
 
@@ -4373,13 +4484,13 @@
         final Rect of = mTmpOverscanFrame;
         final Rect vf = mTmpVisibleFrame;
         final Rect dcf = mTmpDecorFrame;
-        vf.set(displayFrames.mDock);
-        of.set(displayFrames.mDock);
-        df.set(displayFrames.mDock);
-        pf.set(displayFrames.mDock);
+        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;
         dcf.setEmpty();  // Decor frame N/A for system bars.
 
-        if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
+        if (isDefaultDisplay) {
             // 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;
@@ -4398,9 +4509,10 @@
                     && 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(
@@ -4416,32 +4528,30 @@
                 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(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);
+            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);
             if (updateSysUiVisibility) {
                 updateSystemUiVisibilityLw();
             }
         }
-        layoutScreenDecorWindows(displayFrames, pf, df, dcf);
+        layoutScreenDecorWindows(displayId, displayWidth, displayHeight, pf, df, dcf);
     }
 
-    private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) {
+    private void layoutScreenDecorWindows(int displayId, int displayWidth, int displayHeight,
+            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()) {
@@ -4458,10 +4568,10 @@
                 // Docked at left or top.
                 if (frame.bottom >= displayHeight) {
                     // Docked left.
-                    dockFrame.left = Math.max(frame.right, dockFrame.left);
+                    mDockLeft = Math.max(frame.right, mDockLeft);
                 } else if (frame.right >= displayWidth ) {
                     // Docked top.
-                    dockFrame.top = Math.max(frame.bottom, dockFrame.top);
+                    mDockTop = Math.max(frame.bottom, mDockTop);
                 } else {
                     Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
                             + " not docked on left or top of display. frame=" + frame
@@ -4471,10 +4581,10 @@
                 // Docked at right or bottom.
                 if (frame.top <= 0) {
                     // Docked right.
-                    dockFrame.right = Math.min(frame.left, dockFrame.right);
+                    mDockRight = Math.min(frame.left, mDockRight);
                 } else if (frame.left <= 0) {
                     // Docked bottom.
-                    dockFrame.bottom = Math.min(frame.top, dockFrame.bottom);
+                    mDockBottom = Math.min(frame.top, mDockBottom);
                 } else {
                     Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
                             + " not docked on right or bottom" + " of display. frame=" + frame
@@ -4488,165 +4598,194 @@
             }
         }
 
-        displayFrames.mRestricted.set(dockFrame);
-        displayFrames.mCurrent.set(dockFrame);
-        displayFrames.mVoiceContent.set(dockFrame);
-        displayFrames.mSystem.set(dockFrame);
-        displayFrames.mContent.set(dockFrame);
-        displayFrames.mRestrictedOverscan.set(dockFrame);
+        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;
     }
 
-    private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,
-            Rect dcf, int sysui, boolean isKeyguardShowing) {
+    private boolean layoutStatusBar(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) {
-            return false;
-        }
-        // apply any navigation bar insets
-        of.set(displayFrames.mUnrestricted);
-        df.set(displayFrames.mUnrestricted);
-        pf.set(displayFrames.mUnrestricted);
-        vf.set(displayFrames.mStable);
+        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;
 
-        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.
-        displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
+            // For layout, the status bar is always at the top with our fixed height.
+            mStableTop = mUnrestrictedScreenTop + 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.
-            final Rect dockFrame = displayFrames.mDock;
-            dockFrame.top = displayFrames.mStable.top;
-            displayFrames.mContent.set(dockFrame);
-            displayFrames.mVoiceContent.set(dockFrame);
+            // 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 (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
-                    "dock=%s content=%s cur=%s", dockFrame.toString(),
-                    displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
+                mContentTop = mVoiceContentTop = mCurTop = mDockTop;
+                mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
+                mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
+                mContentRight = mVoiceContentRight = mCurRight = mDockRight;
 
-            if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
+                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
                     && !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.
-                displayFrames.mSystem.top = displayFrames.mStable.top;
+                // 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;
             }
         }
-        return mStatusBarController.checkHiddenLw();
+        return false;
     }
 
-    private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
+    private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
+            int uiMode, int overscanLeft, int overscanRight, int overscanBottom, Rect dcf,
             boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
             boolean statusBarExpandedNotKeyguard) {
-        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);
+        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 (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;
+            // 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;
             }
         }
-
-        // 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();
+        return false;
     }
 
     private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
@@ -4674,26 +4813,32 @@
         return 0;
     }
 
-    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) {
+    @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) {
         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.
-            vf.set(displayFrames.mDock);
-            cf.set(displayFrames.mDock);
-            of.set(displayFrames.mDock);
-            df.set(displayFrames.mDock);
+            // 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;
         } 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
@@ -4702,37 +4847,51 @@
                 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()) {
-                    cf.intersectUnchecked(displayFrames.mVoiceContent);
+                    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;
                 } else if (attached.getSurfaceLayer() < mDockLayer) {
-                    cf.intersectUnchecked(displayFrames.mContent);
+                    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;
                 }
             }
             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, 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);
+    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;
+            }
         }
     }
 
@@ -4747,7 +4906,7 @@
 
     /** {@inheritDoc} */
     @Override
-    public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
+    public void layoutWindowLw(WindowState win, WindowState attached) {
         // 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.
@@ -4761,10 +4920,9 @@
                 (win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
         if (needsToOffsetInputMethodTarget) {
             if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
-            offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
+            offsetInputMethodWindowLw(mLastInputMethodWindow);
         }
 
-        final int type = attrs.type;
         final int fl = PolicyControl.getWindowFlags(win, attrs);
         final int pfl = attrs.privateFlags;
         final int sim = attrs.softInputMode;
@@ -4785,83 +4943,120 @@
 
         final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
 
-        sf.set(displayFrames.mStable);
+        if (isDefaultDisplay) {
+            sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom);
+        } else {
+            sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom);
+        }
 
-        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);
+        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;
             // IM dock windows layout below the nav bar...
-            pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
+            pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
             // ...with content insets above the nav bar
-            cf.bottom = vf.bottom = displayFrames.mStable.bottom;
+            cf.bottom = vf.bottom = mStableBottom;
             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 =
-                            displayFrames.mStable.right;
+                    pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
                 } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
-                    pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
+                    pf.left = df.left = of.left = cf.left = vf.left = mStableLeft;
                 }
             }
             // IM dock windows always go to the bottom of the screen.
             attrs.gravity = Gravity.BOTTOM;
             mDockLayer = win.getSurfaceLayer();
-        } else if (type == TYPE_VOICE_INTERACTION) {
-            of.set(displayFrames.mUnrestricted);
-            df.set(displayFrames.mUnrestricted);
-            pf.set(displayFrames.mUnrestricted);
+        } 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;
             if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                cf.set(displayFrames.mDock);
+                cf.left = mDockLeft;
+                cf.top = mDockTop;
+                cf.right = mDockRight;
+                cf.bottom = mDockBottom;
             } else {
-                cf.set(displayFrames.mContent);
+                cf.left = mContentLeft;
+                cf.top = mContentTop;
+                cf.right = mContentRight;
+                cf.bottom = mContentBottom;
             }
             if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                vf.set(displayFrames.mCurrent);
+                vf.left = mCurLeft;
+                vf.top = mCurTop;
+                vf.right = mCurRight;
+                vf.bottom = mCurBottom;
             } else {
                 vf.set(cf);
             }
-        } else if (type == TYPE_WALLPAPER) {
-           layoutWallpaper(displayFrames, pf, df, of, cf);
+        } else if (attrs.type == TYPE_WALLPAPER) {
+           layoutWallpaper(win, pf, df, of, cf);
         } else if (win == mStatusBar) {
-            of.set(displayFrames.mUnrestricted);
-            df.set(displayFrames.mUnrestricted);
-            pf.set(displayFrames.mUnrestricted);
-            cf.set(displayFrames.mStable);
-            vf.set(displayFrames.mStable);
+            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;
 
             if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
-                cf.bottom = displayFrames.mContent.bottom;
+                cf.bottom = mContentBottom;
             } else {
-                cf.bottom = displayFrames.mDock.bottom;
-                vf.bottom = displayFrames.mContent.bottom;
+                cf.bottom = mDockBottom;
+                vf.bottom = mContentBottom;
             }
         } else {
-            dcf.set(displayFrames.mSystem);
-            final boolean inheritTranslucentDecor =
-                    (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
+
+            // 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;
             final boolean isAppWindow =
-                    type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
+                    attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW &&
+                    attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
             final boolean topAtRest =
                     win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
             if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
                 if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
-                        && (fl & FLAG_FULLSCREEN) == 0
-                        && (fl & FLAG_TRANSLUCENT_STATUS) == 0
-                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+                        && (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0
+                        && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0
+                        && (fl & WindowManager.LayoutParams.
+                                FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                         && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
                     // Ensure policy decor includes status bar
-                    dcf.top = displayFrames.mStable.top;
+                    dcf.top = mStableTop;
                 }
-                if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
+                if ((fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0
                         && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
-                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
+                        && (fl & WindowManager.LayoutParams.
+                                FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
                     // Ensure policy decor includes navigation bar
-                    dcf.bottom = displayFrames.mStable.bottom;
-                    dcf.right = displayFrames.mStable.right;
+                    dcf.bottom = mStableBottom;
+                    dcf.right = mStableRight;
                 }
             }
 
@@ -4869,83 +5064,117 @@
                     == (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,
-                            displayFrames);
+                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
                 } else {
-                    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.
+                    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.
                         //
                         // However, they should still dodge the navigation bar if it exists.
 
                         pf.left = df.left = of.left = hasNavBar
-                                ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
-                        pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
+                                ? mDockLeft : mUnrestrictedScreenLeft;
+                        pf.top = df.top = of.top = mUnrestrictedScreenTop;
                         pf.right = df.right = of.right = hasNavBar
-                                ? displayFrames.mRestricted.right
-                                : displayFrames.mUnrestricted.right;
+                                ? mRestrictedScreenLeft+mRestrictedScreenWidth
+                                : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
                         pf.bottom = df.bottom = of.bottom = hasNavBar
-                                ? displayFrames.mRestricted.bottom
-                                : displayFrames.mUnrestricted.bottom;
+                                ? mRestrictedScreenTop+mRestrictedScreenHeight
+                                : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
 
                         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
-                            && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+                            && 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.
-                        of.set(displayFrames.mOverscan);
-                        df.set(displayFrames.mOverscan);
-                        pf.set(displayFrames.mOverscan);
+                        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;
                     } else if (canHideNavigationBar()
                             && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                            && 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 {
-                        df.set(displayFrames.mRestrictedOverscan);
-                        pf.set(displayFrames.mRestrictedOverscan);
+                            && 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.set(displayFrames.mUnrestricted);
+                        of.left = mUnrestrictedScreenLeft;
+                        of.top = mUnrestrictedScreenTop;
+                        of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+                        of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                    } else {
+                        pf.left = df.left = mRestrictedOverscanScreenLeft;
+                        pf.top = df.top = mRestrictedOverscanScreenTop;
+                        pf.right = df.right = mRestrictedOverscanScreenLeft
+                                + mRestrictedOverscanScreenWidth;
+                        pf.bottom = df.bottom = mRestrictedOverscanScreenTop
+                                + mRestrictedOverscanScreenHeight;
+                        // 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;
                     }
 
                     if ((fl & FLAG_FULLSCREEN) == 0) {
                         if (win.isVoiceInteraction()) {
-                            cf.set(displayFrames.mVoiceContent);
+                            cf.left = mVoiceContentLeft;
+                            cf.top = mVoiceContentTop;
+                            cf.right = mVoiceContentRight;
+                            cf.bottom = mVoiceContentBottom;
                         } else {
                             if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                                cf.set(displayFrames.mDock);
+                                cf.left = mDockLeft;
+                                cf.top = mDockTop;
+                                cf.right = mDockRight;
+                                cf.bottom = mDockBottom;
                             } else {
-                                cf.set(displayFrames.mContent);
+                                cf.left = mContentLeft;
+                                cf.top = mContentTop;
+                                cf.right = mContentRight;
+                                cf.bottom = mContentBottom;
                             }
                         }
                     } 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 hiding the status bar to one that is.
-                        cf.set(displayFrames.mRestricted);
+                        // 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;
                     }
-                    applyStableConstraints(sysUiFl, fl, cf, displayFrames);
+                    applyStableConstraints(sysUiFl, fl, cf);
                     if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                        vf.set(displayFrames.mCurrent);
+                        vf.left = mCurLeft;
+                        vf.top = mCurTop;
+                        vf.right = mCurRight;
+                        vf.bottom = mCurBottom;
                     } else {
                         vf.set(cf);
                     }
@@ -4953,70 +5182,86 @@
             } 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 (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 (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 (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 (type == TYPE_VOLUME_OVERLAY) {
+                } else if (attrs.type == TYPE_VOLUME_OVERLAY) {
                     // Volume overlay covers everything, including the status and navbar
-                    cf.set(displayFrames.mUnrestricted);
-                    of.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
+                    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;
                     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 (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
+                } else if (attrs.type == TYPE_NAVIGATION_BAR
+                        || attrs.type == TYPE_NAVIGATION_BAR_PANEL) {
                     // The navigation bar has Real Ultimate Power.
-                    of.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
+                    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;
                     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 ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
+                } else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
+                                || attrs.type == TYPE_BOOT_PROGRESS
+                                || attrs.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.
-                    cf.set(displayFrames.mOverscan);
-                    of.set(displayFrames.mOverscan);
-                    df.set(displayFrames.mOverscan);
-                    pf.set(displayFrames.mOverscan);
-                } else if (type == TYPE_BOOT_PROGRESS) {
+                    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) {
                     // Boot progress screen always covers entire display.
-                    cf.set(displayFrames.mOverscan);
-                    of.set(displayFrames.mOverscan);
-                    df.set(displayFrames.mOverscan);
-                    pf.set(displayFrames.mOverscan);
+                    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 ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
-                        && 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);
+                        && 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;
                 } else if (canHideNavigationBar()
                         && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                        && (type == TYPE_STATUS_BAR
-                            || type == TYPE_TOAST
-                            || type == TYPE_DOCK_DIVIDER
-                            || type == TYPE_VOICE_INTERACTION_STARTING
-                            || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
+                        && (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))) {
                     // 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
@@ -5024,76 +5269,102 @@
                     // 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.
-                    cf.set(displayFrames.mUnrestricted);
-                    of.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
+                    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;
                 } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
-                    of.set(displayFrames.mRestricted);
-                    df.set(displayFrames.mRestricted);
-                    pf.set(displayFrames.mRestricted);
+                    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;
                     if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                        cf.set(displayFrames.mDock);
+                        cf.left = mDockLeft;
+                        cf.top = mDockTop;
+                        cf.right = mDockRight;
+                        cf.bottom = mDockBottom;
                     } else {
-                        cf.set(displayFrames.mContent);
+                        cf.left = mContentLeft;
+                        cf.top = mContentTop;
+                        cf.right = mContentRight;
+                        cf.bottom = mContentBottom;
                     }
                 } else {
-                    cf.set(displayFrames.mRestricted);
-                    of.set(displayFrames.mRestricted);
-                    df.set(displayFrames.mRestricted);
-                    pf.set(displayFrames.mRestricted);
+                    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;
                 }
 
-                applyStableConstraints(sysUiFl, fl, cf,displayFrames);
+                applyStableConstraints(sysUiFl, fl, cf);
 
                 if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                    vf.set(displayFrames.mCurrent);
+                    vf.left = mCurLeft;
+                    vf.top = mCurTop;
+                    vf.right = mCurRight;
+                    vf.bottom = mCurBottom;
                 } 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,
-                        displayFrames);
+                setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf);
             } 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 (type == TYPE_STATUS_BAR_PANEL || type == TYPE_VOLUME_OVERLAY) {
+                if (attrs.type == TYPE_STATUS_BAR_PANEL || attrs.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.
-                    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) {
+                    // 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) {
                     // These dialogs are stable to interim decor changes.
-                    cf.set(displayFrames.mStable);
-                    of.set(displayFrames.mStable);
-                    df.set(displayFrames.mStable);
-                    pf.set(displayFrames.mStable);
+                    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;
                 } else {
-                    pf.set(displayFrames.mContent);
+                    pf.left = mContentLeft;
+                    pf.top = mContentTop;
+                    pf.right = mContentRight;
+                    pf.bottom = mContentBottom;
                     if (win.isVoiceInteraction()) {
-                        cf.set(displayFrames.mVoiceContent);
-                        of.set(displayFrames.mVoiceContent);
-                        df.set(displayFrames.mVoiceContent);
+                        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;
                     } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                        cf.set(displayFrames.mDock);
-                        of.set(displayFrames.mDock);
-                        df.set(displayFrames.mDock);
+                        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;
                     } else {
-                        cf.set(displayFrames.mContent);
-                        of.set(displayFrames.mContent);
-                        df.set(displayFrames.mContent);
+                        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;
                     }
                     if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                        vf.set(displayFrames.mCurrent);
+                        vf.left = mCurLeft;
+                        vf.top = mCurTop;
+                        vf.right = mCurRight;
+                        vf.bottom = mCurBottom;
                     } else {
                         vf.set(cf);
                     }
@@ -5103,11 +5374,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 && type != TYPE_SYSTEM_ERROR
+        if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR
                 && !win.isInMultiWindowMode()) {
             df.left = df.top = -10000;
             df.right = df.bottom = 10000;
-            if (type != TYPE_WALLPAPER) {
+            if (attrs.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;
             }
@@ -5123,7 +5394,7 @@
             osf.set(cf.left, cf.top, cf.right, cf.bottom);
             int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
             if (outset > 0) {
-                int rotation = displayFrames.mRotation;
+                int rotation = mDisplayRotation;
                 if (rotation == Surface.ROTATION_0) {
                     osf.bottom += outset;
                 } else if (rotation == Surface.ROTATION_90) {
@@ -5140,7 +5411,7 @@
 
         if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
                 + ": sim=#" + Integer.toHexString(sim)
-                + " attach=" + attached + " type=" + type
+                + " attach=" + attached + " type=" + attrs.type
                 + String.format(" flags=0x%08x", fl)
                 + " pf=" + pf.toShortString() + " df=" + df.toShortString()
                 + " of=" + of.toShortString()
@@ -5153,42 +5424,62 @@
 
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
-        if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
+        if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleLw()
                 && !win.getGivenInsetsPendingLw()) {
             setLastInputMethodWindowLw(null, null);
-            offsetInputMethodWindowLw(win, displayFrames);
+            offsetInputMethodWindowLw(win);
         }
-        if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
+        if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
                 && !win.getGivenInsetsPendingLw()) {
-            offsetVoiceInputWindowLw(win, displayFrames);
+            offsetVoiceInputWindowLw(win);
         }
     }
 
-    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 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 offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
+    private void offsetInputMethodWindowLw(WindowState win) {
         int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
         top += win.getGivenContentInsetsLw().top;
-        displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
-        displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
+        if (mContentBottom > top) {
+            mContentBottom = top;
+        }
+        if (mVoiceContentBottom > top) {
+            mVoiceContentBottom = top;
+        }
         top = win.getVisibleFrameLw().top;
         top += win.getGivenVisibleInsetsLw().top;
-        displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
+        if (mCurBottom > top) {
+            mCurBottom = top;
+        }
         if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
-                + displayFrames.mDock.bottom + " mContentBottom="
-                + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom);
+                + mDockBottom + " mContentBottom="
+                + mContentBottom + " mCurBottom=" + mCurBottom);
     }
 
-    private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
+    private void offsetVoiceInputWindowLw(WindowState win) {
         int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
         top += win.getGivenContentInsetsLw().top;
-        displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
+        if (mVoiceContentBottom > top) {
+            mVoiceContentBottom = top;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void finishLayoutLw() {
+        return;
     }
 
     /** {@inheritDoc} */
@@ -8041,6 +8332,11 @@
     }
 
     @Override
+    public int getInputMethodWindowVisibleHeightLw() {
+        return mDockBottom - mCurBottom;
+    }
+
+    @Override
     public void setCurrentUserLw(int newUserId) {
         mCurrentUserId = newUserId;
         if (mKeyguardDelegate != null) {
@@ -8129,6 +8425,8 @@
     @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);
     }
 
@@ -8234,6 +8532,58 @@
                 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/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 c4b810f..67d62e1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -104,7 +104,6 @@
 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;
@@ -148,7 +147,6 @@
 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;
@@ -224,8 +222,6 @@
     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()
@@ -289,6 +285,7 @@
     private boolean mLastWallpaperVisible = false;
 
     private Rect mBaseDisplayRect = new Rect();
+    private Rect mContentRect = new Rect();
 
     // Accessed directly by all users.
     private boolean mLayoutNeeded;
@@ -548,7 +545,7 @@
                 w.mLayoutNeeded = false;
                 w.prelayout();
                 final boolean firstLayout = !w.isLaidOut();
-                mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames);
+                mService.mPolicy.layoutWindowLw(w, null);
                 w.mLayoutSeq = mService.mLayoutSeq;
 
                 // If this is the first layout, we need to initialize the last inset values as
@@ -589,7 +586,7 @@
                 }
                 w.mLayoutNeeded = false;
                 w.prelayout();
-                mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
+                mService.mPolicy.layoutWindowLw(w, w.getParentWindow());
                 w.mLayoutSeq = mService.mLayoutSeq;
                 if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
                         + " mContainingFrame=" + w.mContainingFrame
@@ -761,7 +758,6 @@
         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);
@@ -1133,13 +1129,6 @@
         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.
@@ -1755,7 +1744,7 @@
     }
 
     void getContentRect(Rect out) {
-        out.set(mDisplayFrames.mContent);
+        out.set(mContentRect);
     }
 
     TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
@@ -1858,8 +1847,8 @@
             mTmpRect2.setEmpty();
             for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                 final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
-                stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
-                        mDisplayFrames.mContent, mTmpRect2);
+                stack.setTouchExcludeRegion(
+                        focusedTask, delta, mTouchExcludeRegion, mContentRect, 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
@@ -2036,7 +2025,7 @@
         final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
         final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
         final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock();
-        final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
+        final int imeHeight = mService.mPolicy.getInputMethodWindowVisibleHeightLw();
         final boolean imeHeightChanged = imeVisible &&
                 imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
 
@@ -2178,7 +2167,6 @@
         if (screenRotationAnimation != null) {
             screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
         }
-        mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
         proto.end(token);
     }
 
@@ -2258,9 +2246,6 @@
             pw.println(subPrefix
                     + "mInputMethodAnimLayerAdjustment=" + mInputMethodAnimLayerAdjustment);
         }
-
-        pw.println();
-        mDisplayFrames.dump(prefix, pw);
     }
 
     @Override
@@ -2886,22 +2871,21 @@
 
         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);
         }
 
-        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);
+        mService.mPolicy.beginLayoutLw(mDisplayId, dw, dh, mRotation, 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;
@@ -2931,6 +2915,7 @@
             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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f541926..bcb6e673 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);
-            dc.configureDisplayPolicy();
+            mService.configureDisplayPolicyLocked(dc);
 
             // Tap Listeners are supported for:
             // 1. All physical displays (multi-display).
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 df51be1..0652767 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -245,7 +245,6 @@
 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;
 
@@ -1453,19 +1452,23 @@
                 prepareNoneTransitionForRelaunching(atoken);
             }
 
-            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);
+            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;
+                }
             } else {
-                taskBounds = null;
-            }
-            if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayFrames, outContentInsets,
-                    outStableInsets, outOutsets)) {
-                res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
+                outContentInsets.setEmpty();
+                outStableInsets.setEmpty();
             }
 
             if (mInTouchMode) {
@@ -4665,7 +4668,7 @@
         synchronized(mWindowMap) {
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
-            getDefaultDisplayContentLocked().configureDisplayPolicy();
+            configureDisplayPolicyLocked(getDefaultDisplayContentLocked());
         }
 
         try {
@@ -5522,7 +5525,7 @@
         if (!mDisplayReady) {
             return;
         }
-        displayContent.configureDisplayPolicy();
+        configureDisplayPolicyLocked(displayContent);
         displayContent.setLayoutNeeded();
 
         final int displayId = displayContent.getDisplayId();
@@ -5543,6 +5546,18 @@
         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.
@@ -7355,9 +7370,7 @@
         @Override
         public int getInputMethodWindowVisibleHeight() {
             synchronized (mWindowMap) {
-                // TODO(multi-display): Have caller pass in the display they are interested in.
-                final DisplayContent dc = getDefaultDisplayContentLocked();
-                return dc.mDisplayFrames.getInputMethodWindowVisibleHeight();
+                return mPolicy.getInputMethodWindowVisibleHeightLw();
             }
         }
 
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 11f508b..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,63 +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 fd = usb_device_get_fd(device);
-    if (fd < 0) {
-        ALOGE("usb_device_get_fd failed\n");
-        usb_device_close(device);
-        // TODO return an error code here?
-        return 0;
+    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);
+
+    jstring deviceName = env->NewStringUTF(devname);
+    jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer);
+    jstring productName = AndroidRuntime::NewStringLatin1(env, product);
+    jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial);
+
+    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(serialNumber);
+    env->DeleteLocalRef(productName);
+    env->DeleteLocalRef(manufacturerName);
+    env->DeleteLocalRef(deviceName);
+    free(manufacturer);
+    free(product);
+    free(serial);
+
+    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);
+        }
     }
 
-    // from android_hardware_UsbDeviceConnection_get_desc()
-    jbyte rawdescriptors[MAX_DESCRIPTORS_LENGTH];
-    lseek(fd, 0, SEEK_SET);
-    int numBytes = read(fd, rawdescriptors, sizeof(rawdescriptors));
+    env->CallVoidMethod(thiz, method_endUsbDeviceAdded);
 
+fail:
     usb_device_close(device);
-
-    if (numBytes > 0) {
-        JNIEnv* env = AndroidRuntime::getJNIEnv();
-        jobject thiz = (jobject)clientData;
-        jstring deviceAddress = env->NewStringUTF(devAddress);
-
-        jbyteArray descriptorsArray = env->NewByteArray(numBytes);
-        env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawdescriptors);
-
-        env->CallBooleanMethod(thiz, method_usbDeviceAdded,
-                deviceAddress, classID, subClassID, descriptorsArray);
-
-        env->DeleteLocalRef(descriptorsArray);
-        env->DeleteLocalRef(deviceAddress);
-
-        checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    } else {
-        // TODO return an error code here?
-        ALOGE("error reading descriptors\n");
-    }
+    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;
 }
@@ -126,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;
@@ -164,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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 90df82a..74a7bd4a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -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;
@@ -1489,6 +1490,10 @@
             traceBeginAndSlog("StartLauncherAppsService");
             mSystemServiceManager.startService(LauncherAppsService.class);
             traceEnd();
+
+            traceBeginAndSlog("StartCrossProfileAppsService");
+            mSystemServiceManager.startService(CrossProfileAppsService.class);
+            traceEnd();
         }
 
         if (!disableMediaProjection) {
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/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/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/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index f23bd62..9c80544 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -16,12 +16,8 @@
 
 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;
@@ -37,23 +33,16 @@
 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;
@@ -62,6 +51,7 @@
 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;
 
@@ -75,6 +65,7 @@
  */
 // 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
@@ -87,26 +78,22 @@
     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() {
-        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());
+        mWm = mContext.getSystemService(WindowManager.class);
         final Point size = new Point();
-        mDisplay.getDisplay().getRealSize(size);
+        mWm.getDefaultDisplay().getSize(size);
         mDecorThickness = Math.min(size.x, size.y) / 3;
         mHalfDecorThickness = mDecorThickness / 2;
+        mTestActivity = launchActivity(mTestActivityRule);
     }
 
     @After
@@ -114,9 +101,7 @@
         while (!mWindows.isEmpty()) {
             removeWindow(mWindows.get(0));
         }
-        finishActivity(mTestActivity);
-        mDisplay.release();
-        mImageReader.close();
+        finishActivity(mTestActivityRule);
     }
 
     @Test
@@ -274,48 +259,23 @@
         Assert.assertTrue("Excepted " + first + " >= " + second, first >= second);
     }
 
-    private void finishActivity(Activity a) {
-        if (a == null) {
-            return;
-        }
-        a.finish();
+    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 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 53d0bfb..5134c26 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -133,6 +133,11 @@
     }
 
     @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;
     }
@@ -285,11 +290,40 @@
     }
 
     @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) {
 
     }
@@ -548,6 +582,11 @@
     }
 
     @Override
+    public int getInputMethodWindowVisibleHeightLw() {
+        return 0;
+    }
+
+    @Override
     public void setCurrentUserLw(int newUserId) {
 
     }
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/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/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 89ae9e8..027ac8f 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -37,38 +37,39 @@
 
 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 ISerializer {
+ public:
+  ISerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
+
+  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 ~ISerializer() = default;
+
+ protected:
+  IAaptContext* 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)) {
+                                ISerializer* serializer, IArchiveWriter* writer) {
+  // AndroidManifest.xml
+  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 +77,131 @@
           const FileReference* file = ValueCast<FileReference>(config_value->value.get());
           if (file != nullptr) {
             if (file->file == nullptr) {
-              context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource())
+              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 BinarySerializer : public ISerializer {
+ public:
+  BinarySerializer(IAaptContext* context, const Source& source,
+                   const TableFlattenerOptions& options)
+      : ISerializer(context, source), tableFlattenerOptions_(options) {}
+
+  bool SerializeXml(const xml::XmlResource* xml, const std::string& 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, path, ArchiveEntry::kCompress,
+                                        writer);
+  }
+
+  bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) {
+    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) {
+    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 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:
+  TableFlattenerOptions tableFlattenerOptions_;
+
+  DISALLOW_COPY_AND_ASSIGN(BinarySerializer);
+};
+
 class Context : public IAaptContext {
  public:
   Context() : mangler_({}), symbols_(&mangler_) {
@@ -222,7 +295,8 @@
   if (writer == nullptr) {
     return 1;
   }
-  return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1;
+  BinarySerializer serializer(&context, apk->GetSource(), options);
+  return ConvertProtoApkToBinaryApk(&context, std::move(apk), &serializer, writer.get()) ? 0 : 1;
 }
 
 }  // namespace aapt
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/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/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