Merge "Fix SecurityException in Editor.onDrop"
diff --git a/Android.mk b/Android.mk
index 53e892f..1d797c4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -417,7 +417,6 @@
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
-	wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \
 	wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl \
 	wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \
 	wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl \
@@ -430,6 +429,14 @@
 	core/java/android/service/quicksettings/IQSService.aidl \
 	core/java/android/service/quicksettings/IQSTileService.aidl \
 
+# The following are native binders that need to go with the native component
+# at system/update_engine/binder_bindings/. Use relative path to refer to them.
+LOCAL_SRC_FILES += \
+	../../system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl \
+	../../system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl \
+
+LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
+
 # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
 LOCAL_AIDL_INCLUDES += \
       $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \
diff --git a/api/current.txt b/api/current.txt
index b7f63bc..9a1595f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -67,7 +67,7 @@
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
-    field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
+    field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
     field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
@@ -13375,6 +13375,7 @@
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
     field public static final deprecated java.lang.String STRING_TYPE_ORIENTATION = "android.sensor.orientation";
+    field public static final java.lang.String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
     field public static final java.lang.String STRING_TYPE_PRESSURE = "android.sensor.pressure";
     field public static final java.lang.String STRING_TYPE_PROXIMITY = "android.sensor.proximity";
     field public static final java.lang.String STRING_TYPE_RELATIVE_HUMIDITY = "android.sensor.relative_humidity";
@@ -13397,6 +13398,7 @@
     field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
     field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
     field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
+    field public static final int TYPE_POSE_6DOF = 28; // 0x1c
     field public static final int TYPE_PRESSURE = 6; // 0x6
     field public static final int TYPE_PROXIMITY = 8; // 0x8
     field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
@@ -13839,6 +13841,7 @@
     field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1
     field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
+    field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -20855,6 +20858,7 @@
     field public static final int DEFAULT = 0; // 0x0
     field public static final int H263 = 1; // 0x1
     field public static final int H264 = 2; // 0x2
+    field public static final int HEVC = 5; // 0x5
     field public static final int MPEG_4_SP = 3; // 0x3
     field public static final int VP8 = 4; // 0x4
   }
@@ -22214,6 +22218,7 @@
     method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
     method public static final android.net.Uri buildProgramsUriForChannel(long, long, long);
     method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    method public static final android.net.Uri buildRecordedProgramUri(long);
     field public static final java.lang.String AUTHORITY = "android.media.tv";
   }
 
@@ -22350,13 +22355,49 @@
     field public static final java.lang.String TRAVEL = "TRAVEL";
   }
 
+  public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+    field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+    field public static final android.net.Uri CONTENT_URI;
+  }
+
   public final class TvInputInfo implements android.os.Parcelable {
+    method public boolean canRecord();
     method public android.content.Intent createSettingsIntent();
     method public android.content.Intent createSetupIntent();
     method public int describeContents();
     method public java.lang.String getId();
     method public java.lang.String getParentId();
     method public android.content.pm.ServiceInfo getServiceInfo();
+    method public int getTunerCount();
     method public int getType();
     method public boolean isPassthroughInput();
     method public android.graphics.drawable.Drawable loadIcon(android.content.Context);
@@ -22391,6 +22432,10 @@
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
     field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
+    field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
+    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 2; // 0x2
+    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 3; // 0x3
+    field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
     field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
     field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
     field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
@@ -22408,12 +22453,15 @@
     method public void onInputAdded(java.lang.String);
     method public void onInputRemoved(java.lang.String);
     method public void onInputStateChanged(java.lang.String, int);
+    method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
   }
 
   public abstract class TvInputService extends android.app.Service {
     ctor public TvInputService();
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String);
     method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
+    method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
     field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
   }
@@ -22426,6 +22474,18 @@
     method public final boolean onSetSurface(android.view.Surface);
   }
 
+  public static abstract class TvInputService.RecordingSession {
+    ctor public TvInputService.RecordingSession(android.content.Context);
+    method public void notifyConnected();
+    method public void notifyError(int);
+    method public void notifyRecordingStarted();
+    method public void notifyRecordingStopped(android.net.Uri);
+    method public abstract void onConnect(android.net.Uri);
+    method public abstract void onDisconnect();
+    method public abstract void onStartRecording();
+    method public abstract void onStopRecording();
+  }
+
   public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session(android.content.Context);
     method public void layoutSurface(int, int, int, int);
@@ -22453,6 +22513,7 @@
     method public long onTimeShiftGetCurrentPosition();
     method public long onTimeShiftGetStartPosition();
     method public void onTimeShiftPause();
+    method public void onTimeShiftPlay(android.net.Uri);
     method public void onTimeShiftResume();
     method public void onTimeShiftSeekTo(long);
     method public void onTimeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -22463,6 +22524,23 @@
     method public void setOverlayViewEnabled(boolean);
   }
 
+  public class TvRecordingClient {
+    ctor public TvRecordingClient(android.content.Context, java.lang.String, android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
+    method public void connect(java.lang.String, android.net.Uri);
+    method public void disconnect();
+    method public void startRecording();
+    method public void stopRecording();
+  }
+
+  public class TvRecordingClient.RecordingCallback {
+    ctor public TvRecordingClient.RecordingCallback();
+    method public void onConnected();
+    method public void onDisconnected();
+    method public void onError(int);
+    method public void onRecordingStarted();
+    method public void onRecordingStopped(android.net.Uri);
+  }
+
   public final class TvTrackInfo implements android.os.Parcelable {
     method public int describeContents();
     method public final int getAudioChannelCount();
@@ -22514,6 +22592,7 @@
     method public void setStreamVolume(float);
     method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
     method public void timeShiftPause();
+    method public void timeShiftPlay(java.lang.String, android.net.Uri);
     method public void timeShiftResume();
     method public void timeShiftSeekTo(long);
     method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -31021,6 +31100,7 @@
     method public static android.net.Uri buildRootsUri(java.lang.String);
     method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
     method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
+    method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
     method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
     method public static java.lang.String getDocumentId(android.net.Uri);
@@ -31029,6 +31109,7 @@
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     method public static java.lang.String getTreeDocumentId(android.net.Uri);
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
     field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -32855,6 +32936,7 @@
     method public void getVarV(int, android.renderscript.FieldPacker);
     method protected void invoke(int);
     method protected void invoke(int, android.renderscript.FieldPacker);
+    method protected void reduce(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
     method public void setTimeZone(java.lang.String);
     method public void setVar(int, float);
     method public void setVar(int, double);
@@ -35167,6 +35249,7 @@
     method public static boolean hasProperty(int, int);
     method public boolean hasProperty(int);
     method public static java.lang.String propertiesToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
@@ -35307,6 +35390,7 @@
     method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
     method public final void setVideoState(int);
     method public static java.lang.String stateToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -36321,10 +36405,15 @@
     method public int getActiveSubscriptionInfoCountMax();
     method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
     method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+    method public static int getDefaultDataSubscriptionId();
+    method public static int getDefaultSmsSubscriptionId();
+    method public static int getDefaultSubscriptionId();
+    method public static int getDefaultVoiceSubscriptionId();
     method public boolean isNetworkRoaming(int);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
     field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
+    field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
   }
 
   public static class SubscriptionManager.OnSubscriptionsChangedListener {
@@ -36336,32 +36425,49 @@
     method public boolean canChangeDtmfToneLength();
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
+    method public int getCallState(int);
     method public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
+    method public int getDataNetworkType(int);
     method public int getDataState();
     method public java.lang.String getDeviceId();
     method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
+    method public java.lang.String getGroupIdLevel1(int);
     method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+    method public java.lang.String getLine1AlphaTag(int);
     method public java.lang.String getLine1Number();
+    method public java.lang.String getLine1Number(int);
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
     method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public java.lang.String getNetworkCountryIso();
+    method public java.lang.String getNetworkCountryIso(int);
     method public java.lang.String getNetworkOperator();
+    method public java.lang.String getNetworkOperator(int);
     method public java.lang.String getNetworkOperatorName();
+    method public java.lang.String getNetworkOperatorName(int);
     method public int getNetworkType();
+    method public int getNetworkType(int);
     method public int getPhoneCount();
     method public int getPhoneType();
     method public java.lang.String getSimCountryIso();
+    method public java.lang.String getSimCountryIso(int);
     method public java.lang.String getSimOperator();
+    method public java.lang.String getSimOperator(int);
     method public java.lang.String getSimOperatorName();
+    method public java.lang.String getSimOperatorName(int);
     method public java.lang.String getSimSerialNumber();
+    method public java.lang.String getSimSerialNumber(int);
     method public int getSimState();
     method public java.lang.String getSubscriberId();
+    method public java.lang.String getSubscriberId(int);
     method public java.lang.String getVoiceMailAlphaTag();
+    method public java.lang.String getVoiceMailAlphaTag(int);
     method public java.lang.String getVoiceMailNumber();
+    method public java.lang.String getVoiceMailNumber(int);
+    method public int getVoiceNetworkType(int);
     method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
@@ -36372,6 +36478,7 @@
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
+    method public boolean isNetworkRoaming(int);
     method public boolean isSmsCapable();
     method public boolean isTtyModeSupported();
     method public boolean isVoiceCapable();
@@ -36380,9 +36487,11 @@
     method public void listen(android.telephony.PhoneStateListener, int);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
+    method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
+    method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String);
     field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
diff --git a/api/system-current.txt b/api/system-current.txt
index 55f08c8..c2abe38 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -95,7 +95,7 @@
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
     field public static final java.lang.String FORCE_BACK = "android.permission.FORCE_BACK";
     field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
-    field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
+    field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
     field public static final java.lang.String GET_PACKAGE_IMPORTANCE = "android.permission.GET_PACKAGE_IMPORTANCE";
@@ -13772,6 +13772,7 @@
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
     field public static final deprecated java.lang.String STRING_TYPE_ORIENTATION = "android.sensor.orientation";
+    field public static final java.lang.String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
     field public static final java.lang.String STRING_TYPE_PRESSURE = "android.sensor.pressure";
     field public static final java.lang.String STRING_TYPE_PROXIMITY = "android.sensor.proximity";
     field public static final java.lang.String STRING_TYPE_RELATIVE_HUMIDITY = "android.sensor.relative_humidity";
@@ -13795,6 +13796,7 @@
     field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
     field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
     field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
+    field public static final int TYPE_POSE_6DOF = 28; // 0x1c
     field public static final int TYPE_PRESSURE = 6; // 0x6
     field public static final int TYPE_PROXIMITY = 8; // 0x8
     field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
@@ -14240,6 +14242,7 @@
     field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1
     field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
+    field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -22204,6 +22207,7 @@
     field public static final int DEFAULT = 0; // 0x0
     field public static final int H263 = 1; // 0x1
     field public static final int H264 = 2; // 0x2
+    field public static final int HEVC = 5; // 0x5
     field public static final int MPEG_4_SP = 3; // 0x3
     field public static final int VP8 = 4; // 0x4
   }
@@ -23641,6 +23645,7 @@
     method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
     method public static final android.net.Uri buildProgramsUriForChannel(long, long, long);
     method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    method public static final android.net.Uri buildRecordedProgramUri(long);
     method public static final boolean isChannelUriForPassthroughInput(android.net.Uri);
     field public static final java.lang.String AUTHORITY = "android.media.tv";
   }
@@ -23781,6 +23786,40 @@
     field public static final java.lang.String TRAVEL = "TRAVEL";
   }
 
+  public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+    field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+    field public static final android.net.Uri CONTENT_URI;
+  }
+
   public static final class TvContract.WatchedPrograms implements android.media.tv.TvContract.BaseTvColumns {
     field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
@@ -23829,6 +23868,7 @@
   }
 
   public final class TvInputInfo implements android.os.Parcelable {
+    method public boolean canRecord();
     method public android.content.Intent createSettingsIntent();
     method public android.content.Intent createSetupIntent();
     method public static android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -23840,6 +23880,7 @@
     method public java.lang.String getId();
     method public java.lang.String getParentId();
     method public android.content.pm.ServiceInfo getServiceInfo();
+    method public int getTunerCount();
     method public int getType();
     method public boolean isConnectedToHdmiSwitch();
     method public boolean isHardwareInput();
@@ -23874,6 +23915,7 @@
     method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
     method public void addBlockedRating(android.media.tv.TvContentRating);
     method public boolean captureFrame(java.lang.String, android.view.Surface, android.media.tv.TvStreamConfig);
+    method public void createRecordingSession(java.lang.String, android.media.tv.TvInputManager.SessionCallback, android.os.Handler);
     method public void createSession(java.lang.String, android.media.tv.TvInputManager.SessionCallback, android.os.Handler);
     method public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(java.lang.String);
     method public java.util.List<android.media.tv.TvContentRating> getBlockedRatings();
@@ -23897,6 +23939,10 @@
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
     field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
+    field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
+    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 2; // 0x2
+    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 3; // 0x3
+    field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
     field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
     field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
     field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
@@ -23961,16 +24007,19 @@
     method public void onInputRemoved(java.lang.String);
     method public void onInputStateChanged(java.lang.String, int);
     method public void onInputUpdated(java.lang.String);
+    method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
   }
 
   public abstract class TvInputService extends android.app.Service {
     ctor public TvInputService();
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String);
     method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
     method public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo);
     method public java.lang.String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
     method public android.media.tv.TvInputInfo onHdmiDeviceAdded(android.hardware.hdmi.HdmiDeviceInfo);
     method public java.lang.String onHdmiDeviceRemoved(android.hardware.hdmi.HdmiDeviceInfo);
+    method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
     field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
   }
@@ -23983,6 +24032,21 @@
     method public final boolean onSetSurface(android.view.Surface);
   }
 
+  public static abstract class TvInputService.RecordingSession {
+    ctor public TvInputService.RecordingSession(android.content.Context);
+    method public void notifyConnected();
+    method public void notifyError(int);
+    method public void notifyRecordingStarted();
+    method public void notifyRecordingStopped(android.net.Uri);
+    method public void notifySessionEvent(java.lang.String, android.os.Bundle);
+    method public void onAppPrivateCommand(java.lang.String, android.os.Bundle);
+    method public abstract void onConnect(android.net.Uri);
+    method public void onConnect(android.net.Uri, android.os.Bundle);
+    method public abstract void onDisconnect();
+    method public abstract void onStartRecording();
+    method public abstract void onStopRecording();
+  }
+
   public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session(android.content.Context);
     method public void layoutSurface(int, int, int, int);
@@ -24013,6 +24077,7 @@
     method public long onTimeShiftGetCurrentPosition();
     method public long onTimeShiftGetStartPosition();
     method public void onTimeShiftPause();
+    method public void onTimeShiftPlay(android.net.Uri);
     method public void onTimeShiftResume();
     method public void onTimeShiftSeekTo(long);
     method public void onTimeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -24024,6 +24089,26 @@
     method public void setOverlayViewEnabled(boolean);
   }
 
+  public class TvRecordingClient {
+    ctor public TvRecordingClient(android.content.Context, java.lang.String, android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
+    method public void connect(java.lang.String, android.net.Uri);
+    method public void connect(java.lang.String, android.net.Uri, android.os.Bundle);
+    method public void disconnect();
+    method public void sendAppPrivateCommand(java.lang.String, android.os.Bundle);
+    method public void startRecording();
+    method public void stopRecording();
+  }
+
+  public class TvRecordingClient.RecordingCallback {
+    ctor public TvRecordingClient.RecordingCallback();
+    method public void onConnected();
+    method public void onDisconnected();
+    method public void onError(int);
+    method public void onEvent(java.lang.String, java.lang.String, android.os.Bundle);
+    method public void onRecordingStarted();
+    method public void onRecordingStopped(android.net.Uri);
+  }
+
   public class TvStreamConfig implements android.os.Parcelable {
     method public int describeContents();
     method public int getFlags();
@@ -24106,6 +24191,7 @@
     method public void setZOrderMediaOverlay(boolean);
     method public void setZOrderOnTop(boolean);
     method public void timeShiftPause();
+    method public void timeShiftPlay(java.lang.String, android.net.Uri);
     method public void timeShiftResume();
     method public void timeShiftSeekTo(long);
     method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -24401,6 +24487,7 @@
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static deprecated boolean isNetworkTypeValid(int);
+    method public boolean isTetheringSupported();
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
     method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -24413,7 +24500,10 @@
     method public deprecated boolean requestRouteToHost(int, int);
     method public deprecated void setNetworkPreference(int);
     method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
+    method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
+    method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method public deprecated int startUsingNetworkFeature(int, java.lang.String);
+    method public void stopTethering(int);
     method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
     method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     method public void unregisterNetworkCallback(android.app.PendingIntent);
@@ -24435,6 +24525,9 @@
     field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
     field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
     field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+    field public static final int TETHERING_USB = 1; // 0x1
+    field public static final int TETHERING_WIFI = 0; // 0x0
     field public static final int TYPE_BLUETOOTH = 7; // 0x7
     field public static final int TYPE_DUMMY = 8; // 0x8
     field public static final int TYPE_ETHERNET = 9; // 0x9
@@ -24461,6 +24554,12 @@
     method public abstract void onNetworkActive();
   }
 
+  public static abstract class ConnectivityManager.OnStartTetheringCallback {
+    ctor public ConnectivityManager.OnStartTetheringCallback();
+    method public void onTetheringFailed();
+    method public void onTetheringStarted();
+  }
+
   public class Credentials {
     ctor public Credentials(int, int, int);
     method public int getGid();
@@ -33115,6 +33214,7 @@
     method public static android.net.Uri buildRootsUri(java.lang.String);
     method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
     method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
+    method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
     method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
     method public static java.lang.String getDocumentId(android.net.Uri);
@@ -33123,6 +33223,7 @@
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     method public static java.lang.String getTreeDocumentId(android.net.Uri);
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
     field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -35052,6 +35153,7 @@
     method public void getVarV(int, android.renderscript.FieldPacker);
     method protected void invoke(int);
     method protected void invoke(int, android.renderscript.FieldPacker);
+    method protected void reduce(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
     method public void setTimeZone(java.lang.String);
     method public void setVar(int, float);
     method public void setVar(int, double);
@@ -37440,6 +37542,7 @@
     method public static boolean hasProperty(int, int);
     method public boolean hasProperty(int);
     method public static java.lang.String propertiesToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
@@ -37591,6 +37694,7 @@
     method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
     method public final void setVideoState(int);
     method public static java.lang.String stateToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -38690,10 +38794,15 @@
     method public int getActiveSubscriptionInfoCountMax();
     method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
     method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+    method public static int getDefaultDataSubscriptionId();
+    method public static int getDefaultSmsSubscriptionId();
+    method public static int getDefaultSubscriptionId();
+    method public static int getDefaultVoiceSubscriptionId();
     method public boolean isNetworkRoaming(int);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
     field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
+    field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
   }
 
   public static class SubscriptionManager.OnSubscriptionsChangedListener {
@@ -38714,6 +38823,7 @@
     method public boolean endCall();
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
+    method public int getCallState(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
     method public java.lang.String getCdmaMdn();
@@ -38726,30 +38836,46 @@
     method public int getDataActivity();
     method public boolean getDataEnabled();
     method public boolean getDataEnabled(int);
+    method public int getDataNetworkType(int);
     method public int getDataState();
     method public java.lang.String getDeviceId();
     method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
+    method public java.lang.String getGroupIdLevel1(int);
     method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+    method public java.lang.String getLine1AlphaTag(int);
     method public java.lang.String getLine1Number();
+    method public java.lang.String getLine1Number(int);
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
     method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public java.lang.String getNetworkCountryIso();
+    method public java.lang.String getNetworkCountryIso(int);
     method public java.lang.String getNetworkOperator();
+    method public java.lang.String getNetworkOperator(int);
     method public java.lang.String getNetworkOperatorName();
+    method public java.lang.String getNetworkOperatorName(int);
     method public int getNetworkType();
+    method public int getNetworkType(int);
     method public int getPhoneCount();
     method public int getPhoneType();
     method public java.lang.String getSimCountryIso();
+    method public java.lang.String getSimCountryIso(int);
     method public java.lang.String getSimOperator();
+    method public java.lang.String getSimOperator(int);
     method public java.lang.String getSimOperatorName();
+    method public java.lang.String getSimOperatorName(int);
     method public java.lang.String getSimSerialNumber();
+    method public java.lang.String getSimSerialNumber(int);
     method public int getSimState();
     method public java.lang.String getSubscriberId();
+    method public java.lang.String getSubscriberId(int);
     method public java.lang.String getVoiceMailAlphaTag();
+    method public java.lang.String getVoiceMailAlphaTag(int);
     method public java.lang.String getVoiceMailNumber();
+    method public java.lang.String getVoiceMailNumber(int);
+    method public int getVoiceNetworkType(int);
     method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
@@ -38764,6 +38890,7 @@
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isIdle();
     method public boolean isNetworkRoaming();
+    method public boolean isNetworkRoaming(int);
     method public boolean isOffhook();
     method public boolean isRadioOn();
     method public boolean isRinging();
@@ -38779,11 +38906,13 @@
     method public void setDataEnabled(boolean);
     method public void setDataEnabled(int, boolean);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
+    method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
     method public boolean setRadio(boolean);
     method public boolean setRadioPower(boolean);
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
+    method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String);
     method public void silenceRinger();
     method public boolean supplyPin(java.lang.String);
     method public int[] supplyPinReportResult(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index e320b67..d9e8907 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -67,7 +67,7 @@
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
-    field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
+    field public static final deprecated java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
     field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
@@ -13383,6 +13383,7 @@
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field";
     field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated";
     field public static final deprecated java.lang.String STRING_TYPE_ORIENTATION = "android.sensor.orientation";
+    field public static final java.lang.String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
     field public static final java.lang.String STRING_TYPE_PRESSURE = "android.sensor.pressure";
     field public static final java.lang.String STRING_TYPE_PROXIMITY = "android.sensor.proximity";
     field public static final java.lang.String STRING_TYPE_RELATIVE_HUMIDITY = "android.sensor.relative_humidity";
@@ -13405,6 +13406,7 @@
     field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
     field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
     field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
+    field public static final int TYPE_POSE_6DOF = 28; // 0x1c
     field public static final int TYPE_PRESSURE = 6; // 0x6
     field public static final int TYPE_PROXIMITY = 8; // 0x8
     field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
@@ -13847,6 +13849,7 @@
     field public static final int HOT_PIXEL_MODE_FAST = 1; // 0x1
     field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
+    field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -20863,6 +20866,7 @@
     field public static final int DEFAULT = 0; // 0x0
     field public static final int H263 = 1; // 0x1
     field public static final int H264 = 2; // 0x2
+    field public static final int HEVC = 5; // 0x5
     field public static final int MPEG_4_SP = 3; // 0x3
     field public static final int VP8 = 4; // 0x4
   }
@@ -22222,6 +22226,7 @@
     method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
     method public static final android.net.Uri buildProgramsUriForChannel(long, long, long);
     method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+    method public static final android.net.Uri buildRecordedProgramUri(long);
     field public static final java.lang.String AUTHORITY = "android.media.tv";
   }
 
@@ -22358,13 +22363,49 @@
     field public static final java.lang.String TRAVEL = "TRAVEL";
   }
 
+  public static final class TvContract.RecordedPrograms implements android.media.tv.TvContract.BaseTvColumns {
+    field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+    field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+    field public static final java.lang.String COLUMN_CONTENT_RATING = "content_rating";
+    field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+    field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
+    field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+    field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+    field public static final java.lang.String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+    field public static final java.lang.String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+    field public static final java.lang.String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS = "recording_expire_time_utc_millis";
+    field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+    field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
+    field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+    field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+    field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+    field public static final android.net.Uri CONTENT_URI;
+  }
+
   public final class TvInputInfo implements android.os.Parcelable {
+    method public boolean canRecord();
     method public android.content.Intent createSettingsIntent();
     method public android.content.Intent createSetupIntent();
     method public int describeContents();
     method public java.lang.String getId();
     method public java.lang.String getParentId();
     method public android.content.pm.ServiceInfo getServiceInfo();
+    method public int getTunerCount();
     method public int getType();
     method public boolean isPassthroughInput();
     method public android.graphics.drawable.Drawable loadIcon(android.content.Context);
@@ -22399,6 +22440,10 @@
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
     field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
+    field public static final int RECORDING_ERROR_CONNECTION_FAILED = 1; // 0x1
+    field public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 2; // 0x2
+    field public static final int RECORDING_ERROR_RESOURCE_BUSY = 3; // 0x3
+    field public static final int RECORDING_ERROR_UNKNOWN = 0; // 0x0
     field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
     field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
     field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
@@ -22416,12 +22461,15 @@
     method public void onInputAdded(java.lang.String);
     method public void onInputRemoved(java.lang.String);
     method public void onInputStateChanged(java.lang.String, int);
+    method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
   }
 
   public abstract class TvInputService extends android.app.Service {
     ctor public TvInputService();
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String);
     method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
+    method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
     field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
   }
@@ -22434,6 +22482,18 @@
     method public final boolean onSetSurface(android.view.Surface);
   }
 
+  public static abstract class TvInputService.RecordingSession {
+    ctor public TvInputService.RecordingSession(android.content.Context);
+    method public void notifyConnected();
+    method public void notifyError(int);
+    method public void notifyRecordingStarted();
+    method public void notifyRecordingStopped(android.net.Uri);
+    method public abstract void onConnect(android.net.Uri);
+    method public abstract void onDisconnect();
+    method public abstract void onStartRecording();
+    method public abstract void onStopRecording();
+  }
+
   public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session(android.content.Context);
     method public void layoutSurface(int, int, int, int);
@@ -22461,6 +22521,7 @@
     method public long onTimeShiftGetCurrentPosition();
     method public long onTimeShiftGetStartPosition();
     method public void onTimeShiftPause();
+    method public void onTimeShiftPlay(android.net.Uri);
     method public void onTimeShiftResume();
     method public void onTimeShiftSeekTo(long);
     method public void onTimeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -22471,6 +22532,23 @@
     method public void setOverlayViewEnabled(boolean);
   }
 
+  public class TvRecordingClient {
+    ctor public TvRecordingClient(android.content.Context, java.lang.String, android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
+    method public void connect(java.lang.String, android.net.Uri);
+    method public void disconnect();
+    method public void startRecording();
+    method public void stopRecording();
+  }
+
+  public class TvRecordingClient.RecordingCallback {
+    ctor public TvRecordingClient.RecordingCallback();
+    method public void onConnected();
+    method public void onDisconnected();
+    method public void onError(int);
+    method public void onRecordingStarted();
+    method public void onRecordingStopped(android.net.Uri);
+  }
+
   public final class TvTrackInfo implements android.os.Parcelable {
     method public int describeContents();
     method public final int getAudioChannelCount();
@@ -22522,6 +22600,7 @@
     method public void setStreamVolume(float);
     method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
     method public void timeShiftPause();
+    method public void timeShiftPlay(java.lang.String, android.net.Uri);
     method public void timeShiftResume();
     method public void timeShiftSeekTo(long);
     method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
@@ -31033,6 +31112,7 @@
     method public static android.net.Uri buildRootsUri(java.lang.String);
     method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
     method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
+    method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
     method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
     method public static java.lang.String getDocumentId(android.net.Uri);
@@ -31041,6 +31121,7 @@
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     method public static java.lang.String getTreeDocumentId(android.net.Uri);
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
     field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -32869,6 +32950,7 @@
     method public void getVarV(int, android.renderscript.FieldPacker);
     method protected void invoke(int);
     method protected void invoke(int, android.renderscript.FieldPacker);
+    method protected void reduce(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.Script.LaunchOptions);
     method public void setTimeZone(java.lang.String);
     method public void setVar(int, float);
     method public void setVar(int, double);
@@ -35181,6 +35263,7 @@
     method public static boolean hasProperty(int, int);
     method public boolean hasProperty(int);
     method public static java.lang.String propertiesToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 4194304; // 0x400000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
@@ -35321,6 +35404,7 @@
     method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
     method public final void setVideoState(int);
     method public static java.lang.String stateToString(int);
+    field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
@@ -36335,10 +36419,15 @@
     method public int getActiveSubscriptionInfoCountMax();
     method public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
     method public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+    method public static int getDefaultDataSubscriptionId();
+    method public static int getDefaultSmsSubscriptionId();
+    method public static int getDefaultSubscriptionId();
+    method public static int getDefaultVoiceSubscriptionId();
     method public boolean isNetworkRoaming(int);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
     field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
+    field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
   }
 
   public static class SubscriptionManager.OnSubscriptionsChangedListener {
@@ -36350,32 +36439,49 @@
     method public boolean canChangeDtmfToneLength();
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
+    method public int getCallState(int);
     method public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
+    method public int getDataNetworkType(int);
     method public int getDataState();
     method public java.lang.String getDeviceId();
     method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
+    method public java.lang.String getGroupIdLevel1(int);
     method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+    method public java.lang.String getLine1AlphaTag(int);
     method public java.lang.String getLine1Number();
+    method public java.lang.String getLine1Number(int);
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
     method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public java.lang.String getNetworkCountryIso();
+    method public java.lang.String getNetworkCountryIso(int);
     method public java.lang.String getNetworkOperator();
+    method public java.lang.String getNetworkOperator(int);
     method public java.lang.String getNetworkOperatorName();
+    method public java.lang.String getNetworkOperatorName(int);
     method public int getNetworkType();
+    method public int getNetworkType(int);
     method public int getPhoneCount();
     method public int getPhoneType();
     method public java.lang.String getSimCountryIso();
+    method public java.lang.String getSimCountryIso(int);
     method public java.lang.String getSimOperator();
+    method public java.lang.String getSimOperator(int);
     method public java.lang.String getSimOperatorName();
+    method public java.lang.String getSimOperatorName(int);
     method public java.lang.String getSimSerialNumber();
+    method public java.lang.String getSimSerialNumber(int);
     method public int getSimState();
     method public java.lang.String getSubscriberId();
+    method public java.lang.String getSubscriberId(int);
     method public java.lang.String getVoiceMailAlphaTag();
+    method public java.lang.String getVoiceMailAlphaTag(int);
     method public java.lang.String getVoiceMailNumber();
+    method public java.lang.String getVoiceMailNumber(int);
+    method public int getVoiceNetworkType(int);
     method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
@@ -36386,6 +36492,7 @@
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
+    method public boolean isNetworkRoaming(int);
     method public boolean isSmsCapable();
     method public boolean isTtyModeSupported();
     method public boolean isVoiceCapable();
@@ -36394,9 +36501,11 @@
     method public void listen(android.telephony.PhoneStateListener, int);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
+    method public boolean setLine1NumberForDisplay(int, java.lang.String, java.lang.String);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
+    method public boolean setVoiceMailNumber(int, java.lang.String, java.lang.String);
     field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 6dc3cd1..b83484d 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -143,6 +144,10 @@
             mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
             throw e;
         }
+
+        mDevicePolicyManager.setUserProvisioningState(
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
+
         System.out.println("Success: Device owner set to package " + mComponent);
         System.out.println("Active admin set to component " + mComponent.toShortString());
     }
@@ -161,6 +166,10 @@
             mDevicePolicyManager.removeActiveAdmin(mComponent, mUserId);
             throw e;
         }
+
+        mDevicePolicyManager.setUserProvisioningState(
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
+
         System.out.println("Success: Active admin and profile owner set to "
                 + mComponent.toShortString() + " for user " + mUserId);
     }
@@ -180,4 +189,4 @@
             throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 1d9e3bb..35695c4 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -430,47 +430,46 @@
     }
 
     /**
-     * Lists all accounts of any type registered on the device.
-     * Equivalent to getAccountsByType(null).
+     * List every {@link Account} registered on the device that are managed by
+     * applications whose signatures match the caller.
      *
-     * <p>It is safe to call this method from the main thread.
+     * <p>This method can be called safely from the main thread. It is
+     * equivalent to calling <code>getAccountsByType(null)</code>.
      *
-     * <p>Clients of this method that have not been granted the
-     * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
-     * will only see those accounts managed by AbstractAccountAuthenticators whose
-     * signature matches the client.
+     * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their
+     * manifests will continue to behave as they did on devices that support
+     * API level 23. In particular the GET_ACCOUNTS permission is required to
+     * see all the Accounts registered with the AccountManager. See docs for
+     * this function in API level 23 for more information.
      *
-     * @return An array of {@link Account}, one for each account.  Empty
-     *     (never null) if no accounts have been added.
+     * @return Array of Accounts. The array may be empty if no accounts are
+     *     available to the caller.
      */
     @NonNull
-    @RequiresPermission(GET_ACCOUNTS)
     public Account[] getAccounts() {
-        try {
-            return mService.getAccounts(null, mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            // won't ever happen
-            throw new RuntimeException(e);
-        }
+        return getAccountsByType(null);
     }
 
     /**
      * @hide
-     * Lists all accounts of any type registered on the device for a given
-     * user id. Equivalent to getAccountsByType(null).
+     * List every {@link Account} registered on the device for a specific User
+     * that are managed by applications whose signatures match the caller.
      *
-     * <p>It is safe to call this method from the main thread.
+     * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their
+     * manifests will continue to behave as they did on devices that support
+     * API level 23. In particular the GET_ACCOUNTS permission is required to
+     * see all the Accounts registered with the AccountManager for the
+     * specified userId. See docs for this function in API level 23 for more
+     * information.
      *
-     * <p>Clients of this method that have not been granted the
-     * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
-     * will only see those accounts managed by AbstractAccountAuthenticators whose
-     * signature matches the client.
+     * <p>This method can be called safely from the main thread.
      *
-     * @return An array of {@link Account}, one for each account.  Empty
-     *     (never null) if no accounts have been added.
+     * @param int userId associated with the User whose accounts should be
+     *     queried.
+     * @return Array of Accounts. The array may be empty if no accounts are
+     *     available to the caller.
      */
     @NonNull
-    @RequiresPermission(GET_ACCOUNTS)
     public Account[] getAccountsAsUser(int userId) {
         try {
             return mService.getAccountsAsUser(null, userId, mContext.getOpPackageName());
@@ -501,10 +500,11 @@
     /**
      * Returns the accounts visible to the specified package, in an environment where some apps
      * are not authorized to view all accounts. This method can only be called by system apps.
+     *
      * @param type The type of accounts to return, null to retrieve all accounts
      * @param packageName The package name of the app for which the accounts are to be returned
-     * @return An array of {@link Account}, one per matching account.  Empty
-     *     (never null) if no accounts of the specified type have been added.
+     * @return Array of Accounts. The array may be empty if no accounts of th
+     *     specified type are visible to the caller.
      */
     @NonNull
     public Account[] getAccountsByTypeForPackage(String type, String packageName) {
@@ -518,29 +518,22 @@
     }
 
     /**
-     * Lists all accounts of a particular type.  The account type is a
-     * string token corresponding to the authenticator and useful domain
-     * of the account.  For example, there are types corresponding to Google
-     * and Facebook.  The exact string token to use will be published somewhere
-     * associated with the authenticator in question.
+     * List every {@link Account} of a specified type managed by applications
+     * whose signatures match the caller.
      *
-     * <p>It is safe to call this method from the main thread.
+     * <p><b>NOTE:</b> Apps declaring a {@code targetSdkVersion<=23} in their
+     * manifests will continue to behave as they did on devices that support
+     * API level 23. See docs for this function in API level 23 for more
+     * information.
      *
-     * <p>Clients of this method that have not been granted the
-     * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
-     * will only see those accounts managed by AbstractAccountAuthenticators whose
-     * signature matches the client.
+     * <p>This method can be called safely from the main thread.
      *
-     * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before,
-     * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid
-     * or signature match. See docs for this function in API level 22.
-     *
-     * @param type The type of accounts to return, null to retrieve all accounts
-     * @return An array of {@link Account}, one per matching account.  Empty
-     *     (never null) if no accounts of the specified type have been added.
+     * @param type String denoting the type of the accounts to return,
+     *        {@code null} to retrieve all accounts visible to the caller.
+     * @return An array of Accounts.  Empty (never null) if no accounts
+     *         are available to the caller.
      */
     @NonNull
-    @RequiresPermission(GET_ACCOUNTS)
     public Account[] getAccountsByType(String type) {
         return getAccountsByTypeAsUser(type, Process.myUserHandle());
     }
@@ -586,6 +579,7 @@
      * @return a future containing the label string
      * @hide
      */
+    @NonNull
     public AccountManagerFuture<String> getAuthTokenLabel(
             final String accountType, final String authTokenType,
             AccountManagerCallback<String> callback, Handler handler) {
@@ -618,9 +612,13 @@
      * <p>This method may be called from any thread, but the returned
      * {@link AccountManagerFuture} must not be used on the main thread.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#GET_ACCOUNTS} or be a signature
-     * match with the AbstractAccountAuthenticator that manages the account.
+     * <p><b>Note:</b>The specified account must be managed by an application
+     * whose signature matches the caller.
+     *
+     * <p><b>Further note:</b>Apps targeting API level 23 or earlier will continue to
+     * behave as they did on devices that support API level 23. In particular
+     * they may still require the GET_ACCOUNTS permission. See docs for this
+     * function in API level 23.
      *
      * @param account The {@link Account} to test
      * @param features An array of the account features to check
@@ -629,9 +627,11 @@
      * @param handler {@link Handler} identifying the callback thread,
      *     null for the main thread
      * @return An {@link AccountManagerFuture} which resolves to a Boolean,
-     * true if the account exists and has all of the specified features.
+     *     true if the account exists and has all of the specified features.
+     * @throws SecurityException if the specified account is managed by an
+     *     application whose signature doesn't match the caller's signature.
      */
-    @RequiresPermission(GET_ACCOUNTS)
+    @NonNull
     public AccountManagerFuture<Boolean> hasFeatures(final Account account,
             final String[] features,
             AccountManagerCallback<Boolean> callback, Handler handler) {
@@ -654,9 +654,10 @@
 
     /**
      * Lists all accounts of a type which have certain features.  The account
-     * type identifies the authenticator (see {@link #getAccountsByType}).
-     * Account features are authenticator-specific string tokens identifying
-     * boolean account properties (see {@link #hasFeatures}).
+     * type identifies the authenticator (see {@link #getAccountsByType}). Said
+     * authenticator must be in a package whose signature matches the callers
+     * package signature. Account features are authenticator-specific string tokens
+     * identifying boolean account properties (see {@link #hasFeatures}).
      *
      * <p>Unlike {@link #getAccountsByType}, this method calls the authenticator,
      * which may contact the server or do other work to check account features,
@@ -665,19 +666,14 @@
      * <p>This method may be called from any thread, but the returned
      * {@link AccountManagerFuture} must not be used on the main thread.
      *
-     * <p>Clients of this method that have not been granted the
-     * {@link android.Manifest.permission#GET_ACCOUNTS} permission,
-     * will only see those accounts managed by AbstractAccountAuthenticators whose
-     * signature matches the client.
+     * <p><b>NOTE:</b> Apps targeting API level 23 or earlier will continue to
+     * behave as they did on devices that support API level 23. In particular
+     * they may still require the GET_ACCOUNTS permission. See docs for this
+     * function in API level 23.
      *
      * @param type The type of accounts to return, must not be null
      * @param features An array of the account features to require,
      *     may be null or empty
-     *
-     * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before,
-     * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid
-     * or signature match. See docs for this function in API level 22.
-     *
      * @param callback Callback to invoke when the request completes,
      *     null for no callback
      * @param handler {@link Handler} identifying the callback thread,
@@ -686,7 +682,7 @@
      *     {@link Account}, one per account of the specified type which
      *     matches the requested features.
      */
-    @RequiresPermission(GET_ACCOUNTS)
+    @NonNull
     public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
             final String type, final String[] features,
             AccountManagerCallback<Account[]> callback, Handler handler) {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f3e1fc3..dedc8e5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3328,17 +3328,6 @@
                 }
                 r.activity.performResume();
 
-                // If there is a pending relaunch that was requested when the activity was paused,
-                // it will put the activity into paused state when it finally happens. Since the
-                // activity resumed before being relaunched, we don't want that to happen, so we
-                // need to clear the request to relaunch paused.
-                for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
-                    final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
-                    if (relaunching.token == r.token && relaunching.startsNotResumed) {
-                        relaunching.startsNotResumed = false;
-                    }
-                }
-
                 EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED,
                         UserHandle.myUserId(), r.activity.getComponentName().getClassName());
 
@@ -3567,7 +3556,6 @@
     private void handlePauseActivity(IBinder token, boolean finished,
             boolean userLeaving, int configChanges, boolean dontReport, int seq) {
         ActivityClientRecord r = mActivities.get(token);
-        if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq);
         if (!checkAndUpdateLifecycleSeq(seq, r, "pauseActivity")) {
             return;
         }
@@ -4207,7 +4195,6 @@
         synchronized (mResourcesManager) {
             for (int i=0; i<mRelaunchingActivities.size(); i++) {
                 ActivityClientRecord r = mRelaunchingActivities.get(i);
-                if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + this + ", trying: " + r);
                 if (r.token == token) {
                     target = r;
                     if (pendingResults != null) {
@@ -4238,19 +4225,14 @@
             }
 
             if (target == null) {
-                if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null, fromServer:"
-                        + fromServer);
                 target = new ActivityClientRecord();
                 target.token = token;
                 target.pendingResults = pendingResults;
                 target.pendingIntents = pendingNewIntents;
                 target.mPreserveWindow = preserveWindow;
                 if (!fromServer) {
-                    final ActivityClientRecord existing = mActivities.get(token);
-                    if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + existing);
+                    ActivityClientRecord existing = mActivities.get(token);
                     if (existing != null) {
-                        if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: paused= "
-                                + existing.paused);;
                         target.startsNotResumed = existing.paused;
                         target.overrideConfig = existing.overrideConfig;
                     }
@@ -4273,8 +4255,8 @@
             target.pendingConfigChanges |= configChanges;
             target.relaunchSeq = getLifecycleSeq();
         }
-        if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this + ", target "
-                + target + " operation received seq: " + target.relaunchSeq);
+        if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this
+                + " operation received seq: " + target.relaunchSeq);
     }
 
     private void handleRelaunchActivity(ActivityClientRecord tmp) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 368b8ef..3288cd9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,13 +48,12 @@
     boolean areNotificationsEnabledForPackage(String pkg, int uid);
 
     ParceledListSlice getTopics(String pkg, int uid);
-    void setTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic, int visibility);
-    int getTopicVisibilityOverride(String pkg, int uid, in Notification.Topic topic);
-    void setTopicPriority(String pkg, int uid, in Notification.Topic topic, int priority);
-    int getTopicPriority(String pkg, int uid, in Notification.Topic topic);
-    void setTopicImportance(String pkg, int uid, in Notification.Topic topic, int importance);
-    int getTopicImportance(String pkg, int uid, in Notification.Topic topic);
-    void setAppImportance(String pkg, int uid, int importance);
+    void setVisibilityOverride(String pkg, int uid, in Notification.Topic topic, int visibility);
+    int getVisibilityOverride(String pkg, int uid, in Notification.Topic topic);
+    void setPriority(String pkg, int uid, in Notification.Topic topic, int priority);
+    int getPriority(String pkg, int uid, in Notification.Topic topic);
+    void setImportance(String pkg, int uid, in Notification.Topic topic, int importance);
+    int getImportance(String pkg, int uid, in Notification.Topic topic);
     boolean doesAppUseTopics(String pkg, int uid);
 
     // TODO: Remove this when callers have been migrated to the equivalent
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 560e22a..fa11234 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -23,4 +23,11 @@
 
     /** Called whenever an Activity is moved to the pinned stack from another stack. */
     void onActivityPinned();
+
+    /**
+     * Called whenever IActivityManager.startActivity is called on an activity that is already
+     * running in the pinned stack and the activity is not actually started, but the task is either
+     * brought to the front or a new Intent is delivered to it.
+     */
+    void onPinnedActivityRestartAttempt();
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f3b1175..8035c56 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -82,8 +82,6 @@
 import android.net.wifi.nan.WifiNanManager;
 import android.net.wifi.p2p.IWifiP2pManager;
 import android.net.wifi.p2p.WifiP2pManager;
-import android.net.wifi.passpoint.IWifiPasspointManager;
-import android.net.wifi.passpoint.WifiPasspointManager;
 import android.nfc.NfcManager;
 import android.os.BatteryManager;
 import android.os.DropBoxManager;
@@ -483,15 +481,6 @@
                 return new WifiManager(ctx.getOuterContext(), service);
             }});
 
-        registerService(Context.WIFI_PASSPOINT_SERVICE, WifiPasspointManager.class,
-                new CachedServiceFetcher<WifiPasspointManager>() {
-            @Override
-            public WifiPasspointManager createService(ContextImpl ctx) {
-                IBinder b = ServiceManager.getService(Context.WIFI_PASSPOINT_SERVICE);
-                IWifiPasspointManager service = IWifiPasspointManager.Stub.asInterface(b);
-                return new WifiPasspointManager(ctx.getOuterContext(), service);
-            }});
-
         registerService(Context.WIFI_P2P_SERVICE, WifiP2pManager.class,
                 new StaticServiceFetcher<WifiP2pManager>() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7007afe..879d663 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,6 +16,7 @@
 
 package android.app.admin;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -53,6 +54,8 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.InetSocketAddress;
 import java.net.Proxy;
 import java.security.KeyFactory;
@@ -280,6 +283,21 @@
         = "android.app.action.PROVISION_MANAGED_SHAREABLE_DEVICE";
 
     /**
+     * Activity action: Finalizes management provisioning, should be used after user-setup
+     * has been completed and {@link #getUserProvisioningState()} returns one of:
+     * <ul>
+     * <li>{@link #STATE_USER_SETUP_INCOMPLETE}</li>
+     * <li>{@link #STATE_USER_SETUP_COMPLETE}</li>
+     * <li>{@link #STATE_USER_PROFILE_COMPLETE}</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PROVISION_FINALIZATION
+            = "android.app.action.PROVISION_FINALIZATION";
+
+    /**
      * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
      * allows a mobile device management application or NFC programmer application which starts
      * managed provisioning to pass data to the management application instance after provisioning.
@@ -861,6 +879,44 @@
     public static final int PERMISSION_GRANT_STATE_DENIED = 2;
 
     /**
+     * No management for current user in-effect. This is the default.
+     * @hide
+     */
+    public static final int STATE_USER_UNMANAGED = 0;
+
+    /**
+     * Management partially setup, user setup needs to be completed.
+     * @hide
+     */
+    public static final int STATE_USER_SETUP_INCOMPLETE = 1;
+
+    /**
+     * Management partially setup, user setup completed.
+     * @hide
+     */
+    public static final int STATE_USER_SETUP_COMPLETE = 2;
+
+    /**
+     * Management setup and active on current user.
+     * @hide
+     */
+    public static final int STATE_USER_SETUP_FINALIZED = 3;
+
+    /**
+     * Management partially setup on a managed profile.
+     * @hide
+     */
+    public static final int STATE_USER_PROFILE_COMPLETE = 4;
+
+    /**
+     * @hide
+     */
+    @IntDef({STATE_USER_UNMANAGED, STATE_USER_SETUP_INCOMPLETE, STATE_USER_SETUP_COMPLETE,
+            STATE_USER_SETUP_FINALIZED, STATE_USER_PROFILE_COMPLETE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UserProvisioningState {}
+
+    /**
      * Return true if the given administrator component is currently
      * active (enabled) in the system.
      */
@@ -3736,14 +3792,15 @@
 
     /**
      * Start Quick Contact on the managed profile for the user, if the policy allows.
+     *
      * @hide
      */
     public void startManagedQuickContact(String actualLookupKey, long actualContactId,
-            long directoryId, Intent originalIntent) {
+            boolean isContactIdIgnored, long directoryId, Intent originalIntent) {
         if (mService != null) {
             try {
-                mService.startManagedQuickContact(
-                        actualLookupKey, actualContactId, directoryId, originalIntent);
+                mService.startManagedQuickContact(actualLookupKey, actualContactId,
+                        isContactIdIgnored, directoryId, originalIntent);
             } catch (RemoteException e) {
                 Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
@@ -3756,7 +3813,7 @@
      */
     public void startManagedQuickContact(String actualLookupKey, long actualContactId,
             Intent originalIntent) {
-        startManagedQuickContact(actualLookupKey, actualContactId, Directory.DEFAULT,
+        startManagedQuickContact(actualLookupKey, actualContactId, false, Directory.DEFAULT,
                 originalIntent);
     }
 
@@ -5247,6 +5304,40 @@
     }
 
     /**
+     * @return the {@link UserProvisioningState} for the current user - for unmanaged users will
+     *         return {@link #STATE_USER_UNMANAGED}
+     * @hide
+     */
+    @UserProvisioningState
+    public int getUserProvisioningState() {
+        if (mService != null) {
+            try {
+                return mService.getUserProvisioningState();
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return STATE_USER_UNMANAGED;
+    }
+
+    /**
+     * Set the {@link UserProvisioningState} for the supplied user, if they are managed.
+     *
+     * @param state to store
+     * @param userHandle for user
+     * @hide
+     */
+    public void setUserProvisioningState(@UserProvisioningState int state, int userHandle) {
+        if (mService != null) {
+            try {
+                mService.setUserProvisioningState(state, userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
      * @hide
      * Indicates the entity that controls the device or profile owner. A user/profile is considered
      * affiliated if it is managed by the same entity as the device.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e97bdf2b..b34e678 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -211,7 +211,7 @@
     void setCrossProfileContactsSearchDisabled(in ComponentName who, boolean disabled);
     boolean getCrossProfileContactsSearchDisabled(in ComponentName who);
     boolean getCrossProfileContactsSearchDisabledForUser(int userId);
-    void startManagedQuickContact(String lookupKey, long contactId, long directoryId, in Intent originalIntent);
+    void startManagedQuickContact(String lookupKey, long contactId, boolean isContactIdIgnored, long directoryId, in Intent originalIntent);
 
     void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled);
     boolean getBluetoothContactSharingDisabled(in ComponentName who);
@@ -269,6 +269,9 @@
     int getOrganizationColor(in ComponentName admin);
     int getOrganizationColorForUser(int userHandle);
 
+    int getUserProvisioningState();
+    void setUserProvisioningState(int state, int userHandle);
+
     void setAffiliationIds(in ComponentName admin, in List<String> ids);
     boolean isAffiliatedUser();
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 2bbc54f..999d826 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -155,7 +155,7 @@
     }
 
     /**
-     * @return whether {@userId} has enabled and configured trust agents. Ignores short-term
+     * @return whether {@param userId} has enabled and configured trust agents. Ignores short-term
      * unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}.
      */
     @RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 74cb0f6..9cd7d05 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -104,8 +104,6 @@
     void getActivityEnergyInfoFromController();
     BluetoothActivityEnergyInfo reportActivityInfo();
 
-    // For dumpsys support
-    void dump(in ParcelFileDescriptor fd);
     void onLeServiceUp();
     void onBrEdrDown();
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 48d0196..9df7a28 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2561,7 +2561,6 @@
             //@hide: NETWORK_POLICY_SERVICE,
             WIFI_SERVICE,
             WIFI_NAN_SERVICE,
-            WIFI_PASSPOINT_SERVICE,
             WIFI_P2P_SERVICE,
             WIFI_SCANNING_SERVICE,
             //@hide: WIFI_RTT_SERVICE,
@@ -3003,17 +3002,6 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
-     * android.net.wifi.passpoint.WifiPasspointManager} for handling management of
-     * Wi-Fi passpoint access.
-     *
-     * @see #getSystemService
-     * @see android.net.wifi.passpoint.WifiPasspointManager
-     * @hide
-     */
-    public static final String WIFI_PASSPOINT_SERVICE = "wifipasspoint";
-
-    /**
-     * Use with {@link #getSystemService} to retrieve a {@link
      * android.net.wifi.p2p.WifiP2pManager} for handling management of
      * Wi-Fi peer-to-peer connections.
      *
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0168908..654396b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -176,7 +176,7 @@
     
     /**
      * Value for {@link #flags}: this is set if this application has been
-     * install as an update to a built-in system application.
+     * installed as an update to a built-in system application.
      */
     public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7;
     
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 6935174..e5efd56 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -551,6 +551,29 @@
      */
     public static final String STRING_TYPE_DEVICE_ORIENTATION = "android.sensor.device_orientation";
 
+     /**
+     * A constant describing a pose sensor with 6 degrees of freedom.
+     *
+     * Similar to {@link #TYPE_ROTATION_VECTOR}, with additional delta
+     * translation from an arbitrary reference point.
+     *
+     * Can use camera, depth sensor etc to compute output value.
+     *
+     * This is expected to be a high power sensor and expected only to be
+     * used when the screen is on.
+     *
+     * Expected to be more accurate than the rotation vector alone.
+     *
+     */
+    public static final int TYPE_POSE_6DOF = 28;
+
+    /**
+     * A constant string describing a pose sensor with 6 degrees of freedom.
+     *
+     * @see #TYPE_POSE_6DOF
+     */
+    public static final String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
+
     /**
      * A constant describing all sensor types.
      */
@@ -637,6 +660,7 @@
             1, // SENSOR_TYPE_PICK_UP_GESTURE
             1, // SENSOR_TYPE_WRIST_TILT_GESTURE
             1, // SENSOR_TYPE_DEVICE_ORIENTATION
+            16, // SENSOR_TYPE_POSE_6DOF
     };
 
     /**
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 9937b2c..416c74c 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -497,6 +497,40 @@
      * <li> 3: device is rotated 90 degrees clockwise from default orientation (X axis
      *         is vertical and points down)
      * </ul>
+     *
+     *   <h4>{@link android.hardware.Sensor#TYPE_POSE_6DOF
+     * Sensor.TYPE_POSE_6DOF}:</h4>
+     *
+     * A TYPE_POSE_6DOF event consists of a rotation expressed as a quaternion and a translation
+     * expressed in SI units. The event also contains a delta rotation and translation that show
+     * how the device?s pose has changed since the previous sequence numbered pose.
+     * The event uses the cannonical Android Sensor axes.
+     *
+     *
+     * <ul>
+     *  <li> values[0]: x*sin(&#952/2) </li>
+     *  <li> values[1]: y*sin(&#952/2) </li>
+     *  <li> values[2]: z*sin(&#952/2) </li>
+     *  <li> values[3]: cos(&#952/2)   </li>
+     *
+     *
+     * <li> values[4]: Translation along x axis from an arbitrary origin. </li>
+     * li> values[5]: Translation along y axis from an arbitrary origin. </li>
+     * <li> values[6]: Translation along z axis from an arbitrary origin. </li>
+     *
+     * <li> values[7]:  Delta quaternion rotation x*sin(&#952/2) </li>
+     * <li> values[8]:  Delta quaternion rotation y*sin(&#952/2) </li>
+     * <li> values[9]:  Delta quaternion rotation z*sin(&#952/2) </li>
+     * <li> values[10]: Delta quaternion rotation cos(&#952/2) </li>
+     *
+     * <li> values[11]: Delta translation along x axis. </li>
+     * <li> values[12]: Delta translation along y axis. </li>
+     * <li> values[13]: Delta translation along z axis. </li>
+     *
+     * <li> values[14]: Sequence number </li>
+     *
+     * </ul>
+     *
      */
     public final float[] values;
 
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 2695dfd..2aa6af6 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2787,22 +2787,39 @@
 
     /**
      * <p>Generally classifies the overall set of the camera device functionality.</p>
-     * <p>Camera devices will come in three flavors: LEGACY, LIMITED and FULL.</p>
-     * <p>A FULL device will support below capabilities:</p>
+     * <p>The supported hardware level is a high-level description of the camera device's
+     * capabilities, summarizing several capabilities into one field.  Each level adds additional
+     * features to the previous one, and is always a strict superset of the previous level.
+     * The ordering is <code>LEGACY &lt; LIMITED &lt; FULL &lt; LEVEL_3</code>.</p>
+     * <p>Starting from <code>LEVEL_3</code>, the level enumerations are guaranteed to be in increasing
+     * numerical value as well. To check if a given device is at least at a given hardware level,
+     * the following code snippet can be used:</p>
+     * <pre><code>// Returns true if the device supports the required hardware level, or better.
+     * boolean isHardwareLevelSupported(CameraCharacteristics c, int requiredLevel) {
+     *     int deviceLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+     *     if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
+     *         return requiredLevel == deviceLevel;
+     *     }
+     *     // deviceLevel is not LEGACY, can use numerical sort
+     *     return requiredLevel &lt;= deviceLevel;
+     * }
+     * </code></pre>
+     * <p>At a high level, the levels are:</p>
      * <ul>
-     * <li>BURST_CAPTURE capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains BURST_CAPTURE)</li>
-     * <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li>
-     * <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR)</li>
-     * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
-     *   MANUAL_POST_PROCESSING)</li>
-     * <li>At least 3 processed (but not stalling) format output streams
-     *   ({@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC android.request.maxNumOutputProc} <code>&gt;=</code> 3)</li>
-     * <li>The required stream configurations defined in android.scaler.availableStreamConfigurations</li>
-     * <li>The required exposure time range defined in {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
-     * <li>The required maxFrameDuration defined in {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
+     * <li><code>LEGACY</code> devices operate in a backwards-compatibility mode for older
+     *   Android devices, and have very limited capabilities.</li>
+     * <li><code>LIMITED</code> devices represent the
+     *   baseline feature set, and may also include additional capabilities that are
+     *   subsets of <code>FULL</code>.</li>
+     * <li><code>FULL</code> devices additionally support per-frame manual control of sensor, flash, lens and
+     *   post-processing settings, and image capture at a high rate.</li>
+     * <li><code>LEVEL_3</code> devices additionally support YUV reprocessing and RAW image capture, along
+     *   with additional output stream configurations.</li>
      * </ul>
-     * <p>A LIMITED device may have some or none of the above characteristics.
-     * To find out more refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
+     * <p>See the individual level enums for full descriptions of the supported capabilities.  The
+     * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} entry describes the device's capabilities at a
+     * finer-grain level, if needed. In addition, many controls have their available settings or
+     * ranges defined in individual {@link android.hardware.camera2.CameraCharacteristics } entries.</p>
      * <p>Some features are not part of any particular hardware level or capability and must be
      * queried separately. These include:</p>
      * <ul>
@@ -2813,19 +2830,12 @@
      *   ({@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization},
      *    {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes})</li>
      * </ul>
-     * <p>A LEGACY device does not support per-frame control, manual sensor control, manual
-     * post-processing, arbitrary cropping regions, and has relaxed performance constraints.</p>
-     * <p>Each higher level supports everything the lower level supports
-     * in this order: FULL <code>&gt;</code> LIMITED <code>&gt;</code> LEGACY.</p>
-     * <p>Note:
-     * Pre-API level 23, FULL devices also supported arbitrary cropping region
-     * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>==</code> FREEFORM); this requirement was relaxed in API level 23,
-     * and FULL devices may only support CENTERED cropping.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}</li>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li>
+     *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_3 3}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -2833,16 +2843,12 @@
      * @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION
      * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
-     * @see CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_PROC
-     * @see CameraCharacteristics#SCALER_CROPPING_TYPE
-     * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
-     * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
      * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
      * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES
-     * @see CameraCharacteristics#SYNC_MAX_LATENCY
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
+     * @see #INFO_SUPPORTED_HARDWARE_LEVEL_3
      */
     @PublicKey
     public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 63bcb31..28bb22a 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -314,7 +314,7 @@
      * </table><br>
      * </p>
      *
-     * <p>Limited-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+     * <p>Limited-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
      * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices
      * support at least the following stream combinations in addition to those for
      * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY} devices:
@@ -332,13 +332,13 @@
      * </table><br>
      * </p>
      *
-     * <p>FULL-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+     * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
      * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices
      * support at least the following stream combinations in addition to those for
      * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices:
      *
      * <table>
-     * <tr><th colspan="7">FULL-capability additional guaranteed configurations</th></tr>
+     * <tr><th colspan="7">FULL-level additional guaranteed configurations</th></tr>
      * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
      * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution GPU processing with preview.</td> </tr>
@@ -389,6 +389,22 @@
      * </table><br>
      * </p>
      *
+     * <p>LEVEL-3 ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+     * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL_3})
+     * support at least the following stream combinations in addition to the combinations for
+     * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} and for
+     * RAW capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}):
+     *
+     * <table>
+     * <tr><th colspan="11">LEVEL-3 additional guaranteed configurations</th></tr>
+     * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>In-app viewfinder analysis with dynamic selection of output format.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>In-app viewfinder analysis with dynamic selection of output format.</td> </tr>
+     * </table><br>
+     * </p>
+     *
      * <p>Since the capabilities of camera devices vary greatly, a given camera device may support
      * target combinations with sizes outside of these guarantees, but this can only be tested for
      * by attempting to create a session with such targets.</p>
@@ -501,7 +517,7 @@
      *  #rb { border-right-width: thick; }
      * </style>
      *
-     * <p>Limited-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+     * <p>LIMITED-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
      * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices
      * support at least the following stream combinations for creating a reprocessable capture
      * session in addition to those listed in {@link #createCaptureSession createCaptureSession} for
@@ -518,14 +534,14 @@
      * </table><br>
      * </p>
      *
-     * <p>FULL-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+     * <p>FULL-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
      * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices
      * support at least the following stream combinations for creating a reprocessable capture
      * session in addition to those for
      * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices:
      *
      * <table>
-     * <tr><th colspan="11">FULL-capability additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
+     * <tr><th colspan="11">FULL-level additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
      * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
      * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
      * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>Maximum-resolution multi-frame image fusion in-app processing with regular preview.</td> </tr>
@@ -555,6 +571,22 @@
      * </table><br>
      * </p>
      *
+     * <p>LEVEL-3 ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+     * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL_3}) devices
+     * support at least the following stream combinations for creating a reprocessable capture
+     * session in addition to those for
+     * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} devices. Note that targets in the "Reprocess-only target" column may only be
+     * used as an output target for a reprocess capture request, not as an output to a regular capture request.
+     *
+     * <table>
+     * <tr><th colspan="13">LEVEL-3 additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is always guaranteed.</th></tr>
+     * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th colspan="2" id="rb">Reprocess-only target</th><th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>In-app viewfinder analysis with ZSL and RAW.</td> </tr>
+     * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code 640x480}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td>In-app viewfinder analysis with ZSL, RAW, and JPEG reprocessing output.</td> </tr>
+     * </table><br>
+     * </p>
+     *
      * @param inputConfig The configuration for the input {@link Surface}
      * @param outputs The new set of Surfaces that should be made available as
      *                targets for captured image data.
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index f61892e..d58ad22 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -964,23 +964,102 @@
     //
 
     /**
-     * <p>This camera device has only limited capabilities.</p>
+     * <p>This camera device does not have enough capabilities to qualify as a <code>FULL</code> device or
+     * better.</p>
+     * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code> tables in the
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p>
+     * <p>All <code>LIMITED</code> devices support the <code>BACKWARDS_COMPATIBLE</code> capability, indicating basic
+     * support for color image capture. The only exception is that the device may
+     * alternatively support only the <code>DEPTH_OUTPUT</code> capability, if it can only output depth
+     * measurements and not color images.</p>
+     * <p><code>LIMITED</code> devices and above require the use of {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}
+     * to lock exposure metering (and calculate flash power, for cameras with flash) before
+     * capturing a high-quality still image.</p>
+     * <p>A <code>LIMITED</code> device that only lists the <code>BACKWARDS_COMPATIBLE</code> capability is only
+     * required to support full-automatic operation and post-processing (<code>OFF</code> is not
+     * supported for {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}, or
+     * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode})</p>
+     * <p>Additional capabilities may optionally be supported by a <code>LIMITED</code>-level device, and
+     * can be checked for in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_MODE
+     * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+     * @see CaptureRequest#CONTROL_AF_MODE
+     * @see CaptureRequest#CONTROL_AWB_MODE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      */
     public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0;
 
     /**
      * <p>This camera device is capable of supporting advanced imaging applications.</p>
+     * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code> tables in the
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p>
+     * <p>A <code>FULL</code> device will support below capabilities:</p>
+     * <ul>
+     * <li><code>BURST_CAPTURE</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     *   <code>BURST_CAPTURE</code>)</li>
+     * <li>Per frame control ({@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} <code>==</code> PER_FRAME_CONTROL)</li>
+     * <li>Manual sensor control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains <code>MANUAL_SENSOR</code>)</li>
+     * <li>Manual post-processing control ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     *   <code>MANUAL_POST_PROCESSING</code>)</li>
+     * <li>The required exposure time range defined in {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</li>
+     * <li>The required maxFrameDuration defined in {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
+     * </ul>
+     * <p>Note:
+     * Pre-API level 23, FULL devices also supported arbitrary cropping region
+     * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>== FREEFORM</code>); this requirement was relaxed in API level
+     * 23, and <code>FULL</code> devices may only support <code>CENTERED</code> cropping.</p>
+     *
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraCharacteristics#SCALER_CROPPING_TYPE
+     * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
+     * @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
+     * @see CameraCharacteristics#SYNC_MAX_LATENCY
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      */
     public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1;
 
     /**
      * <p>This camera device is running in backward compatibility mode.</p>
+     * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession}
+     * documentation are supported.</p>
+     * <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual
+     * post-processing, arbitrary cropping regions, and has relaxed performance constraints.
+     * No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a
+     * <code>LEGACY</code> device in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
+     * <p>In addition, the {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is not functional on <code>LEGACY</code>
+     * devices. Instead, every request that includes a JPEG-format output target is treated
+     * as triggering a still capture, internally executing a precapture trigger.  This may
+     * fire the flash for flash power metering during precapture, and then fire the flash
+     * for the final capture, if a flash is available on the device and the AE mode is set to
+     * enable the flash.</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      */
     public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2;
 
+    /**
+     * <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to
+     * FULL-level capabilities.</p>
+     * <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and
+     * <code>LIMITED</code> tables in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession}
+     * documentation are guaranteed to be supported.</p>
+     * <p>The following additional capabilities are guaranteed to be supported:</p>
+     * <ul>
+     * <li><code>YUV_REPROCESSING</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     *   <code>YUV_REPROCESSING</code>)</li>
+     * <li><code>RAW</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     *   <code>RAW</code>)</li>
+     * </ul>
+     *
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     */
+    public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3;
+
     //
     // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY
     //
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index c3cae65..79eff4f 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1599,13 +1599,13 @@
      * <p>This key is optional. Applications can assume there is no boost applied
      * after RAW is captured if this key is not available.
      * When this key is available, the sensitivity boost value must be within
-     * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}.</p>
+     * {@link CameraCharacteristics#CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE android.control.postRawSensitivityBoostRange}.</p>
      * <p>If the camera device cannot apply the exact boost requested, it will reduce the
      * boost to the nearest supported value.
      * The final boost value used will be available in the output capture result.</p>
      * <p>For devices that support post RAW sensitivity boost, the YUV/JPEG output images
      * of such device will have the total sensitivity of
-     * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * android.control.ispSensitivity / 100</code>
+     * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost} / 100</code>
      * The sensitivity of RAW format images will always be <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</code></p>
      * <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
      * OFF; otherwise the auto-exposure algorithm will override this value.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 7b9d1a3..5748726 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2106,13 +2106,13 @@
      * <p>This key is optional. Applications can assume there is no boost applied
      * after RAW is captured if this key is not available.
      * When this key is available, the sensitivity boost value must be within
-     * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}.</p>
+     * {@link CameraCharacteristics#CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE android.control.postRawSensitivityBoostRange}.</p>
      * <p>If the camera device cannot apply the exact boost requested, it will reduce the
      * boost to the nearest supported value.
      * The final boost value used will be available in the output capture result.</p>
      * <p>For devices that support post RAW sensitivity boost, the YUV/JPEG output images
      * of such device will have the total sensitivity of
-     * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * android.control.ispSensitivity / 100</code>
+     * <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost} / 100</code>
      * The sensitivity of RAW format images will always be <code>{@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}</code></p>
      * <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
      * OFF; otherwise the auto-exposure algorithm will override this value.</p>
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 04caa8f..c96c9be 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -496,7 +496,10 @@
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
     public void enroll(byte [] token, CancellationSignal cancel, int flags,
-            EnrollmentCallback callback) {
+            EnrollmentCallback callback, int userId) {
+        if (userId == UserHandle.USER_CURRENT) {
+            userId = getCurrentUserId();
+        }
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
@@ -512,7 +515,7 @@
 
         if (mService != null) try {
             mEnrollmentCallback = callback;
-            mService.enroll(mToken, token, getCurrentUserId(), mServiceReceiver, flags);
+            mService.enroll(mToken, token, userId, mServiceReceiver, flags);
         } catch (RemoteException e) {
             Log.w(TAG, "Remote exception in enroll: ", e);
             if (callback != null) {
@@ -556,6 +559,21 @@
     }
 
     /**
+     * Sets the active user. This is meant to be used to select the current profile for enrollment
+     * to allow separate enrolled fingers for a work profile
+     * @param userId
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FINGERPRINT)
+    public void setActiveUser(int userId) {
+        if (mService != null) try {
+            mService.setActiveUser(userId);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Remote exception in setActiveUser: ", e);
+        }
+    }
+
+    /**
      * Remove given fingerprint template from fingerprint hardware and/or protected storage.
      * @param fp the fingerprint item to remove
      * @param callback an optional callback to verify that fingerprint templates have been
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 690a751..43d5577 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -75,4 +75,7 @@
 
     // Add a callback which gets notified when the fingerprint lockout period expired.
     void addLockoutResetCallback(IFingerprintServiceLockoutResetCallback callback);
+
+    // Explicitly set the active user (for enrolling work profile)
+    void setActiveUser(int uid);
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 08c0c09..92c721b 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,6 +16,7 @@
 package android.net;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -27,6 +28,7 @@
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -37,6 +39,7 @@
 import android.os.Messenger;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
@@ -338,6 +341,71 @@
     public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED";
 
     /**
+     * Invalid tethering type.
+     * @see #startTethering(int, OnStartTetheringCallback, boolean)
+     * @hide
+     */
+    public static final int TETHERING_INVALID   = -1;
+
+    /**
+     * Wifi tethering type.
+     * @see #startTethering(int, OnStartTetheringCallback, boolean)
+     * @hide
+     */
+    @SystemApi
+    public static final int TETHERING_WIFI      = 0;
+
+    /**
+     * USB tethering type.
+     * @see #startTethering(int, OnStartTetheringCallback, boolean)
+     * @hide
+     */
+    @SystemApi
+    public static final int TETHERING_USB       = 1;
+
+    /**
+     * Bluetooth tethering type.
+     * @see #startTethering(int, OnStartTetheringCallback, boolean)
+     * @hide
+     */
+    @SystemApi
+    public static final int TETHERING_BLUETOOTH = 2;
+
+    /**
+     * Extra used for communicating with the TetherService. Includes the type of tethering to
+     * enable if any.
+     * @hide
+     */
+    public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+
+    /**
+     * Extra used for communicating with the TetherService. Includes the type of tethering for
+     * which to cancel provisioning.
+     * @hide
+     */
+    public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+
+    /**
+     * Extra used for communicating with the TetherService. True to schedule a recheck of tether
+     * provisioning.
+     * @hide
+     */
+    public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+
+    /**
+     * Tells the TetherService to run a provision check now.
+     * @hide
+     */
+    public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+
+    /**
+     * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
+     * which will receive provisioning results. Can be left empty.
+     * @hide
+     */
+    public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+
+    /**
      * The absence of a connection type.
      * @hide
      */
@@ -1543,7 +1611,7 @@
         if (b != null) {
             try {
                 ITelephony it = ITelephony.Stub.asInterface(b);
-                int subId = SubscriptionManager.getDefaultDataSubId();
+                int subId = SubscriptionManager.getDefaultDataSubscriptionId();
                 Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
                 boolean retVal = it.getDataEnabled(subId);
                 Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
@@ -1789,6 +1857,11 @@
      * or the ability to modify system settings as determined by
      * {@link android.provider.Settings.System#canWrite}.</p>
      *
+     * <p>WARNING: New clients should not use this function. The only usages should be in PanService
+     * and WifiStateMachine which need direct access. All other clients should use
+     * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+     * logic.</p>
+     *
      * @param iface the interface name to tether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
      *
@@ -1810,6 +1883,11 @@
      * or the ability to modify system settings as determined by
      * {@link android.provider.Settings.System#canWrite}.</p>
      *
+     * <p>WARNING: New clients should not use this function. The only usages should be in PanService
+     * and WifiStateMachine which need direct access. All other clients should use
+     * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+     * logic.</p>
+     *
      * @param iface the interface name to untether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
      *
@@ -1834,6 +1912,7 @@
      *
      * {@hide}
      */
+    @SystemApi
     public boolean isTetheringSupported() {
         try {
             return mService.isTetheringSupported();
@@ -1843,6 +1922,94 @@
     }
 
     /**
+     * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
+     * @hide
+     */
+    @SystemApi
+    public static abstract class OnStartTetheringCallback {
+        /**
+         * Called when tethering has been successfully started.
+         */
+        public void onTetheringStarted() {};
+
+        /**
+         * Called when starting tethering failed.
+         */
+        public void onTetheringFailed() {};
+    }
+
+    /**
+     * Convenient overload for
+     * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null
+     * handler to run on the current thread's {@link Looper}.
+     * @hide
+     */
+    @SystemApi
+    public void startTethering(int type, boolean showProvisioningUi,
+            final OnStartTetheringCallback callback) {
+        startTethering(type, showProvisioningUi, callback, null);
+    }
+
+    /**
+     * Runs tether provisioning for the given type if needed and then starts tethering if
+     * the check succeeds. If no carrier provisioning is required for tethering, tethering is
+     * enabled immediately. If provisioning fails, tethering will not be enabled. It also
+     * schedules tether provisioning re-checks if appropriate.
+     *
+     * @param type The type of tethering to start. Must be one of
+     *         {@link ConnectivityManager.TETHERING_WIFI},
+     *         {@link ConnectivityManager.TETHERING_USB}, or
+     *         {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+     * @param showProvisioningUi a boolean indicating to show the provisioning app UI if there
+     *         is one. This should be true the first time this function is called and also any time
+     *         the user can see this UI. It gives users information from their carrier about the
+     *         check failing and how they can sign up for tethering if possible.
+     * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller
+     *         of the result of trying to tether.
+     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+     * @hide
+     */
+    @SystemApi
+    public void startTethering(int type, boolean showProvisioningUi,
+            final OnStartTetheringCallback callback, Handler handler) {
+        ResultReceiver wrappedCallback = new ResultReceiver(handler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode == TETHER_ERROR_NO_ERROR) {
+                    callback.onTetheringStarted();
+                } else {
+                    callback.onTetheringFailed();
+                }
+            }
+        };
+        try {
+            mService.startTethering(type, wrappedCallback, showProvisioningUi);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception trying to start tethering.", e);
+            wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
+        }
+    }
+
+    /**
+     * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
+     * applicable.
+     *
+     * @param type The type of tethering to stop. Must be one of
+     *         {@link ConnectivityManager.TETHERING_WIFI},
+     *         {@link ConnectivityManager.TETHERING_USB}, or
+     *         {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+     * @hide
+     */
+    @SystemApi
+    public void stopTethering(int type) {
+        try {
+            mService.stopTethering(type);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception trying to stop tethering.", e);
+        }
+    }
+
+    /**
      * Get the list of regular expressions that define any tetherable
      * USB network interfaces.  If USB tethering is not supported by the
      * device, this list should be empty.
@@ -1949,6 +2116,8 @@
     public static final int TETHER_ERROR_DISABLE_NAT_ERROR    = 9;
     /** {@hide} */
     public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
+    /** {@hide} */
+    public static final int TETHER_ERROR_PROVISION_FAILED     = 11;
 
     /**
      * Get a more detailed error code after a Tethering or Untethering
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 569468e..1a9c9ea 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -76,6 +76,10 @@
 
     boolean isTetheringSupported();
 
+    void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi);
+
+    void stopTethering(int type);
+
     String[] getTetherableIfaces();
 
     String[] getTetheredIfaces();
diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java
index 777baab..66181e0 100644
--- a/core/java/android/print/PrintJob.java
+++ b/core/java/android/print/PrintJob.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import java.util.Objects;
+
 /**
  * This class represents a print job from the perspective of an
  * application. It contains behavior methods for performing operations
@@ -30,11 +32,11 @@
  */
 public final class PrintJob {
 
-    private final PrintManager mPrintManager;
+    private final @NonNull PrintManager mPrintManager;
 
-    private PrintJobInfo mCachedInfo;
+    private @NonNull PrintJobInfo mCachedInfo;
 
-    PrintJob(PrintJobInfo info, PrintManager printManager) {
+    PrintJob(@NonNull PrintJobInfo info, @NonNull PrintManager printManager) {
         mCachedInfo = info;
         mPrintManager = printManager;
     }
@@ -44,7 +46,7 @@
      *
      * @return The id.
      */
-    public @NonNull PrintJobId getId() {
+    public @Nullable PrintJobId getId() {
         return mCachedInfo.getId();
     }
 
@@ -58,7 +60,7 @@
      *
      * @return The print job info.
      */
-    public @Nullable PrintJobInfo getInfo() {
+    public @NonNull PrintJobInfo getInfo() {
         if (isInImmutableState()) {
             return mCachedInfo;
         }
@@ -193,11 +195,17 @@
             return false;
         }
         PrintJob other = (PrintJob) obj;
-        return mCachedInfo.getId().equals(other.mCachedInfo.getId());
+        return Objects.equals(mCachedInfo.getId(), other.mCachedInfo.getId());
     }
 
     @Override
     public int hashCode() {
-        return mCachedInfo.getId().hashCode();
+        PrintJobId printJobId = mCachedInfo.getId();
+
+        if (printJobId == null) {
+            return 0;
+        } else {
+            return printJobId.hashCode();
+        }
     }
 }
diff --git a/core/java/android/print/PrintJobId.java b/core/java/android/print/PrintJobId.java
index a2ee02b..186ae9b 100644
--- a/core/java/android/print/PrintJobId.java
+++ b/core/java/android/print/PrintJobId.java
@@ -19,7 +19,8 @@
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
 
 import java.util.UUID;
 
@@ -27,7 +28,7 @@
  * This class represents the id of a print job.
  */
 public final class PrintJobId implements Parcelable {
-    private final String mValue;
+    private final @NonNull String mValue;
 
     /**
      * Creates a new instance.
@@ -45,7 +46,7 @@
      *
      * @hide
      */
-    public PrintJobId(String value) {
+    public PrintJobId(@NonNull String value) {
         mValue = value;
     }
 
@@ -53,7 +54,7 @@
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + ((mValue != null) ? mValue.hashCode() : 0);
+        result = prime * result + mValue.hashCode();
         return result;
     }
 
@@ -69,7 +70,7 @@
             return false;
         }
         PrintJobId other = (PrintJobId) obj;
-        if (!TextUtils.equals(mValue, other.mValue)) {
+        if (!mValue.equals(other.mValue)) {
             return false;
         }
         return true;
@@ -104,7 +105,7 @@
      *
      * @hide
      */
-    public static PrintJobId unflattenFromString(String string) {
+    public static @NonNull PrintJobId unflattenFromString(@NonNull String string) {
         return new PrintJobId(string);
     }
 
@@ -112,7 +113,7 @@
             new Parcelable.Creator<PrintJobId>() {
         @Override
         public PrintJobId createFromParcel(Parcel parcel) {
-            return new PrintJobId(parcel.readString());
+            return new PrintJobId(Preconditions.checkNotNull(parcel.readString()));
         }
 
         @Override
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 21836b3..7e3a72f 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -244,7 +244,7 @@
      *
      * @return The id.
      */
-    public @NonNull PrintJobId getId() {
+    public @Nullable PrintJobId getId() {
         return mId;
     }
 
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 3eb4874..58f260c 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -535,7 +535,10 @@
         return new PrinterDiscoverySession(mService, mContext, mUserId);
     }
 
-    private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
+    /**
+     * @hide
+     */
+    public static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
             implements ActivityLifecycleCallbacks {
         private final Object mLock = new Object();
 
@@ -1061,7 +1064,10 @@
         }
     }
 
-    private static final class PrintJobStateChangeListenerWrapper extends
+    /**
+     * @hide
+     */
+    public static final class PrintJobStateChangeListenerWrapper extends
             IPrintJobStateChangeListener.Stub {
         private final WeakReference<PrintJobStateChangeListener> mWeakListener;
         private final WeakReference<Handler> mWeakHandler;
diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java
index abb441b..c587edd 100644
--- a/core/java/android/print/PrinterDiscoverySession.java
+++ b/core/java/android/print/PrinterDiscoverySession.java
@@ -16,6 +16,8 @@
 
 package android.print;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.Handler;
@@ -72,7 +74,7 @@
         }
     }
 
-    public final void startPrinterDiscovery(List<PrinterId> priorityList) {
+    public final void startPrinterDiscovery(@Nullable List<PrinterId> priorityList) {
         if (isDestroyed()) {
             Log.w(LOG_TAG, "Ignoring start printers discovery - session destroyed");
             return;
@@ -102,7 +104,7 @@
         }
     }
 
-    public final void startPrinterStateTracking(PrinterId printerId) {
+    public final void startPrinterStateTracking(@NonNull PrinterId printerId) {
         if (isDestroyed()) {
             Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed");
             return;
@@ -114,7 +116,7 @@
         }
     }
 
-    public final void stopPrinterStateTracking(PrinterId printerId) {
+    public final void stopPrinterStateTracking(@NonNull PrinterId printerId) {
         if (isDestroyed()) {
             Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed");
             return;
@@ -285,7 +287,7 @@
         }
     }
 
-    private static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub {
+    public static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub {
 
         private final WeakReference<PrinterDiscoverySession> mWeakSession;
 
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 3104492..62d214e 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -16,6 +16,7 @@
 
 package android.printservice;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Service;
 import android.content.ComponentName;
@@ -346,7 +347,7 @@
      * @param localId A locally unique id in the context of your print service.
      * @return Global printer id.
      */
-    public final PrinterId generatePrinterId(String localId) {
+    public @NonNull final PrinterId generatePrinterId(String localId) {
         throwIfNotCalledOnMainThread();
         localId = Preconditions.checkNotNull(localId, "localId cannot be null");
         return new PrinterId(new ComponentName(getPackageName(),
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index b547432..1bfb3c0 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8401,10 +8401,15 @@
          * @hide
          */
         public static Intent rebuildManagedQuickContactsIntent(String lookupKey, long contactId,
-                long directoryId, Intent originalIntent) {
+                boolean isContactIdIgnored, long directoryId, Intent originalIntent) {
             final Intent intent = new Intent(ACTION_QUICK_CONTACT);
             // Rebuild the URI from a lookup key and a contact ID.
-            Uri uri = Contacts.getLookupUri(contactId, lookupKey);
+            Uri uri = null;
+            if (!TextUtils.isEmpty(lookupKey)) {
+                uri = isContactIdIgnored
+                        ? Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey)
+                        : Contacts.getLookupUri(contactId, lookupKey);
+            }
             if (uri != null && directoryId != Directory.DEFAULT) {
                 uri = uri.buildUpon().appendQueryParameter(
                         ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
index 36ef52d..2cd1d48 100644
--- a/core/java/android/provider/ContactsInternal.java
+++ b/core/java/android/provider/ContactsInternal.java
@@ -42,10 +42,12 @@
     private static final UriMatcher sContactsUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 
     private static final int CONTACTS_URI_LOOKUP_ID = 1000;
+    private static final int CONTACTS_URI_LOOKUP = 1001;
 
     static {
         // Contacts URI matching table
         final UriMatcher matcher = sContactsUriMatcher;
+        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_URI_LOOKUP);
         matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_URI_LOOKUP_ID);
     }
 
@@ -57,6 +59,7 @@
 
         final int match = sContactsUriMatcher.match(uri);
         switch (match) {
+            case CONTACTS_URI_LOOKUP:
             case CONTACTS_URI_LOOKUP_ID: {
                 if (maybeStartManagedQuickContact(context, intent)) {
                     return; // Request handled by DPM.  Just return here.
@@ -89,7 +92,10 @@
 
         // Decompose into an ID and a lookup key.
         final List<String> pathSegments = uri.getPathSegments();
-        final long contactId = ContentUris.parseId(uri);
+        final boolean isContactIdIgnored = pathSegments.size() < 4;
+        final long contactId = isContactIdIgnored
+                ? ContactsContract.Contacts.ENTERPRISE_CONTACT_ID_BASE //contact id will be ignored
+                : ContentUris.parseId(uri);
         final String lookupKey = pathSegments.get(2);
         final String directoryIdStr = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
         final long directoryId = (directoryIdStr == null)
@@ -119,8 +125,8 @@
         final long actualDirectoryId = (directoryId
                 - ContactsContract.Directory.ENTERPRISE_DIRECTORY_ID_BASE);
 
-        dpm.startManagedQuickContact(actualLookupKey, actualContactId, actualDirectoryId,
-                originalIntent);
+        dpm.startManagedQuickContact(actualLookupKey, actualContactId, isContactIdIgnored,
+                actualDirectoryId, originalIntent);
         return true;
     }
 }
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 88cc8a2..cdd88f6 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -342,8 +342,8 @@
          * within the same document provider.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri)
-         * @see DocumentsProvider#moveDocument(String, String)
+         * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri, Uri)
+         * @see DocumentsProvider#moveDocument(String, String, String)
          */
         public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
 
@@ -614,6 +614,8 @@
     public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument";
 
     /** {@hide} */
+    public static final String EXTRA_PARENT_URI = "parentUri";
+    /** {@hide} */
     public static final String EXTRA_URI = "uri";
 
     private static final String PATH_ROOT = "root";
@@ -1139,7 +1141,6 @@
      * @param targetParentDocumentUri document which will become a parent of the source
      *         document's copy.
      * @return the copied document, or {@code null} if failed.
-     * @hide
      */
     public static Uri copyDocument(ContentResolver resolver, Uri sourceDocumentUri,
             Uri targetParentDocumentUri) {
@@ -1170,17 +1171,18 @@
      * Moves the given document under a new parent.
      *
      * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_MOVE}
+     * @param sourceParentDocumentUri parent document of the document to move.
      * @param targetParentDocumentUri document which will become a new parent of the source
      *         document.
      * @return the moved document, or {@code null} if failed.
-     * @hide
      */
     public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri,
-            Uri targetParentDocumentUri) {
+            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) {
         final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
                 sourceDocumentUri.getAuthority());
         try {
-            return moveDocument(client, sourceDocumentUri, targetParentDocumentUri);
+            return moveDocument(client, sourceParentDocumentUri, sourceDocumentUri,
+                    targetParentDocumentUri);
         } catch (Exception e) {
             Log.w(TAG, "Failed to move document", e);
             return null;
@@ -1191,9 +1193,10 @@
 
     /** {@hide} */
     public static Uri moveDocument(ContentProviderClient client, Uri sourceDocumentUri,
-            Uri targetParentDocumentUri) throws RemoteException {
+            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws RemoteException {
         final Bundle in = new Bundle();
         in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
+        in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri);
         in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
 
         final Bundle out = client.call(METHOD_MOVE_DOCUMENT, null, in);
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 94b4157..bae928d 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -289,12 +289,14 @@
      * be returned.
      *
      * @param sourceDocumentId the document to move.
+     * @param sourceParentDocumentId the parent of the document to move.
      * @param targetParentDocumentId the target document to be a new parent of the
      *     source document.
      * @hide
      */
     @SuppressWarnings("unused")
-    public String moveDocument(String sourceDocumentId, String targetParentDocumentId)
+    public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
+            String targetParentDocumentId)
             throws FileNotFoundException {
         throw new UnsupportedOperationException("Move not supported");
     }
@@ -759,7 +761,7 @@
 
                 out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
 
-                // Original document no longer exists, clean up any grants
+                // Original document no longer exists, clean up any grants.
                 revokeDocumentPermission(documentId);
             }
 
@@ -767,7 +769,7 @@
             enforceWritePermissionInner(documentUri, getCallingPackage(), null);
             deleteDocument(documentId);
 
-            // Document no longer exists, clean up any grants
+            // Document no longer exists, clean up any grants.
             revokeDocumentPermission(documentId);
 
         } else if (METHOD_COPY_DOCUMENT.equals(method)) {
@@ -793,13 +795,16 @@
             }
 
         } else if (METHOD_MOVE_DOCUMENT.equals(method)) {
+            final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
+            final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
             final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
             final String targetId = DocumentsContract.getDocumentId(targetUri);
 
-            enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+            enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+            enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null);
             enforceWritePermissionInner(targetUri, getCallingPackage(), null);
 
-            final String newDocumentId = moveDocument(documentId, targetId);
+            final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
 
             if (newDocumentId != null) {
                 final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
@@ -814,7 +819,7 @@
                 out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
             }
 
-            // Original document no longer exists, clean up any grants
+            // Original document no longer exists, clean up any grants.
             revokeDocumentPermission(documentId);
 
         } else {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 80cf4bb..a9fd3c8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -129,6 +129,27 @@
             "android.settings.WIRELESS_SETTINGS";
 
     /**
+     * Activity Action: Show tether provisioning activity.
+     *
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: {@link ConnectivityManager.EXTRA_TETHER_TYPE} should be included to specify which type
+     * of tethering should be checked. {@link ConnectivityManager.EXTRA_PROVISION_CALLBACK} should
+     * contain a {@link ResultReceiver} which will be called back with a tether result code.
+     * <p>
+     * Output: The result of the provisioning check.
+     * {@link ConnectivityManager.TETHER_ERROR_NO_ERROR} if successful,
+     * {@link ConnectivityManager.TETHER_ERROR_PROVISION_FAILED} for failure.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_TETHER_PROVISIONING =
+            "android.settings.TETHER_PROVISIONING_UI";
+
+    /**
      * Activity Action: Show settings to allow entering/exiting airplane mode.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
index 7e3601e..f3272e4 100644
--- a/core/java/android/security/net/config/CertificateSource.java
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -16,12 +16,13 @@
 
 package android.security.net.config;
 
-import java.util.Set;
 import java.security.cert.X509Certificate;
+import java.util.Set;
 
 /** @hide */
 public interface CertificateSource {
     Set<X509Certificate> getCertificates();
     X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
     X509Certificate findByIssuerAndSignature(X509Certificate cert);
+    Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert);
 }
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index ff728ef..742d430 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -17,8 +17,8 @@
 package android.security.net.config;
 
 import android.util.ArraySet;
-import java.util.Set;
 import java.security.cert.X509Certificate;
+import java.util.Set;
 
 /** @hide */
 public final class CertificatesEntryRef {
@@ -60,4 +60,8 @@
 
         return new TrustAnchor(foundCert, mOverridesPins);
     }
+
+    public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) {
+        return mSource.findAllByIssuerAndSignature(cert);
+    }
 }
diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
index bf29efa..b2c068c 100644
--- a/core/java/android/security/net/config/DirectoryCertificateSource.java
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -29,6 +29,7 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.Collections;
 import java.util.Set;
 import libcore.io.IoUtils;
 
@@ -110,10 +111,50 @@
         });
     }
 
+    @Override
+    public Set<X509Certificate> findAllByIssuerAndSignature(final X509Certificate cert) {
+        return findCerts(cert.getIssuerX500Principal(), new CertSelector() {
+            @Override
+            public boolean match(X509Certificate ca) {
+                try {
+                    cert.verify(ca.getPublicKey());
+                    return true;
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        });
+    }
+
     private static interface CertSelector {
         boolean match(X509Certificate cert);
     }
 
+    private Set<X509Certificate> findCerts(X500Principal subj, CertSelector selector) {
+        String hash = getHash(subj);
+        Set<X509Certificate> certs = null;
+        for (int index = 0; index >= 0; index++) {
+            String fileName = hash + "." + index;
+            if (!new File(mDir, fileName).exists()) {
+                break;
+            }
+            if (isCertMarkedAsRemoved(fileName)) {
+                continue;
+            }
+            X509Certificate cert = readCertificate(fileName);
+            if (!subj.equals(cert.getSubjectX500Principal())) {
+                continue;
+            }
+            if (selector.match(cert)) {
+                if (certs == null) {
+                    certs = new ArraySet<X509Certificate>();
+                }
+                certs.add(cert);
+            }
+        }
+        return certs != null ? certs : Collections.<X509Certificate>emptySet();
+    }
+
     private X509Certificate findCert(X500Principal subj, CertSelector selector) {
         String hash = getHash(subj);
         for (int index = 0; index >= 0; index++) {
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
index b6105cd..ba5dd83 100644
--- a/core/java/android/security/net/config/KeyStoreCertificateSource.java
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -21,6 +21,7 @@
 import java.security.KeyStoreException;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Set;
 
@@ -90,4 +91,18 @@
         }
         return anchor.getTrustedCert();
     }
+
+    @Override
+    public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) {
+        ensureInitialized();
+        Set<java.security.cert.TrustAnchor> anchors = mIndex.findAllByIssuerAndSignature(cert);
+        if (anchors.isEmpty()) {
+            return Collections.<X509Certificate>emptySet();
+        }
+        Set<X509Certificate> certs = new ArraySet<X509Certificate>(anchors.size());
+        for (java.security.cert.TrustAnchor anchor : anchors) {
+            certs.add(anchor.getTrustedCert());
+        }
+        return certs;
+    }
 }
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 0a2edff..ebe14691 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -145,6 +145,15 @@
         return null;
     }
 
+    /** @hide */
+    public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) {
+        Set<X509Certificate> certs = new ArraySet<X509Certificate>();
+        for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+            certs.addAll(ref.findAllCertificatesByIssuerAndSignature(cert));
+        }
+        return certs;
+    }
+
     /**
      * Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
      *
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index e489c2c..8803c4b 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -25,6 +25,7 @@
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Set;
 
 import com.android.org.conscrypt.TrustedCertificateIndex;
@@ -100,4 +101,18 @@
         }
         return anchor.getTrustedCert();
     }
+
+    @Override
+    public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) {
+        ensureInitialized();
+        Set<java.security.cert.TrustAnchor> anchors = mIndex.findAllByIssuerAndSignature(cert);
+        if (anchors.isEmpty()) {
+            return Collections.<X509Certificate>emptySet();
+        }
+        Set<X509Certificate> certs = new ArraySet<X509Certificate>(anchors.size());
+        for (java.security.cert.TrustAnchor anchor : anchors) {
+            certs.add(anchor.getTrustedCert());
+        }
+        return certs;
+    }
 }
diff --git a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
index 4a90f82..c2f29be 100644
--- a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
+++ b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
@@ -42,6 +42,11 @@
     }
 
     @Override
+    public Set<X509Certificate> findAllIssuers(X509Certificate cert) {
+        return mConfig.findAllCertificatesByIssuerAndSignature(cert);
+    }
+
+    @Override
     public X509Certificate getTrustAnchor(X509Certificate cert) {
         TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert);
         if (anchor == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6c3f308..dd0887f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -100,13 +100,13 @@
 import android.widget.Checkable;
 import android.widget.FrameLayout;
 import android.widget.ScrollBarDrawable;
-
 import static android.os.Build.VERSION_CODES.*;
 import static java.lang.Math.max;
 
 import com.android.internal.R;
 import com.android.internal.util.Predicate;
 import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.ScrollBarUtils;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
@@ -5128,6 +5128,88 @@
         return mVerticalScrollbarPosition;
     }
 
+    boolean isOnScrollbar(float x, float y) {
+        if (mScrollCache == null) {
+            return false;
+        }
+        x += getScrollX();
+        y += getScrollY();
+        if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
+            final Rect bounds = mScrollCache.mScrollBarBounds;
+            getVerticalScrollBarBounds(bounds);
+            if (bounds.contains((int)x, (int)y)) {
+                return true;
+            }
+        }
+        if (isHorizontalScrollBarEnabled()) {
+            final Rect bounds = mScrollCache.mScrollBarBounds;
+            getHorizontalScrollBarBounds(bounds);
+            if (bounds.contains((int)x, (int)y)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    boolean isOnScrollbarThumb(float x, float y) {
+        return isOnVerticalScrollbarThumb(x, y) || isOnHorizontalScrollbarThumb(x, y);
+    }
+
+    private boolean isOnVerticalScrollbarThumb(float x, float y) {
+        if (mScrollCache == null) {
+            return false;
+        }
+        if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
+            x += getScrollX();
+            y += getScrollY();
+            final Rect bounds = mScrollCache.mScrollBarBounds;
+            getVerticalScrollBarBounds(bounds);
+            final int range = computeVerticalScrollRange();
+            final int offset = computeVerticalScrollOffset();
+            final int extent = computeVerticalScrollExtent();
+            final int thumbLength = ScrollBarUtils.getThumbLength(bounds.height(), bounds.width(),
+                    extent, range);
+            final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.height(), thumbLength,
+                    extent, range, offset);
+            final int thumbTop = bounds.top + thumbOffset;
+            if (x >= bounds.left && x <= bounds.right && y >= thumbTop
+                    && y <= thumbTop + thumbLength) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isOnHorizontalScrollbarThumb(float x, float y) {
+        if (mScrollCache == null) {
+            return false;
+        }
+        if (isHorizontalScrollBarEnabled()) {
+            x += getScrollX();
+            y += getScrollY();
+            final Rect bounds = mScrollCache.mScrollBarBounds;
+            getHorizontalScrollBarBounds(bounds);
+            final int range = computeHorizontalScrollRange();
+            final int offset = computeHorizontalScrollOffset();
+            final int extent = computeHorizontalScrollExtent();
+            final int thumbLength = ScrollBarUtils.getThumbLength(bounds.width(), bounds.height(),
+                    extent, range);
+            final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.width(), thumbLength,
+                    extent, range, offset);
+            final int thumbLeft = bounds.left + thumbOffset;
+            if (x >= thumbLeft && x <= thumbLeft + thumbLength && y >= bounds.top
+                    && y <= bounds.bottom) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    boolean isDraggingScrollBar() {
+        return mScrollCache != null
+                && mScrollCache.mScrollBarDraggingState != ScrollabilityCache.NOT_DRAGGING;
+    }
+
     /**
      * Sets the state of all scroll indicators.
      * <p>
@@ -5795,8 +5877,11 @@
      * <p>A View should call this if it maintains some notion of which part
      * of its content is interesting.  For example, a text editing view
      * should call this when its cursor moves.
+     * <p>The Rectangle passed into this method should be in the View's content coordinate space.
+     * It should not be affected by which part of the View is currently visible or its scroll
+     * position.
      *
-     * @param rectangle The rectangle.
+     * @param rectangle The rectangle in the View's content coordinate space
      * @return Whether any parent scrolled.
      */
     public boolean requestRectangleOnScreen(Rect rectangle) {
@@ -5810,11 +5895,13 @@
      * <p>A View should call this if it maintains some notion of which part
      * of its content is interesting.  For example, a text editing view
      * should call this when its cursor moves.
-     *
+     * <p>The Rectangle passed into this method should be in the View's content coordinate space.
+     * It should not be affected by which part of the View is currently visible or its scroll
+     * position.
      * <p>When <code>immediate</code> is set to true, scrolling will not be
      * animated.
      *
-     * @param rectangle The rectangle.
+     * @param rectangle The rectangle in the View's content coordinate space
      * @param immediate True to forbid animated scrolling, false otherwise
      * @return Whether any parent scrolled.
      */
@@ -5834,24 +5921,16 @@
             rectangle.set((int) position.left, (int) position.top,
                     (int) position.right, (int) position.bottom);
 
-            scrolled |= parent.requestChildRectangleOnScreen(child,
-                    rectangle, immediate);
-
-            if (!child.hasIdentityMatrix()) {
-                child.getMatrix().mapRect(position);
-            }
-
-            position.offset(child.mLeft, child.mTop);
+            scrolled |= parent.requestChildRectangleOnScreen(child, rectangle, immediate);
 
             if (!(parent instanceof View)) {
                 break;
             }
 
-            View parentView = (View) parent;
+            // move it from child's content coordinate space to parent's content coordinate space
+            position.offset(child.mLeft - child.getScrollX(), child.mTop -child.getScrollY());
 
-            position.offset(-parentView.getScrollX(), -parentView.getScrollY());
-
-            child = parentView;
+            child = (View) parent;
             parent = child.getParent();
         }
 
@@ -9718,6 +9797,9 @@
         }
 
         if (onFilterTouchEventForSecurity(event)) {
+            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
+                result = true;
+            }
             //noinspection SimplifiableIfStatement
             ListenerInfo li = mListenerInfo;
             if (li != null && li.mOnTouchListener != null
@@ -10582,6 +10664,11 @@
             }
         }
 
+        if ((action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_MOVE)
+                && event.isFromSource(InputDevice.SOURCE_MOUSE)
+                && isOnScrollbar(event.getX(), event.getY())) {
+            awakenScrollBars();
+        }
         if (isHoverable()) {
             switch (action) {
                 case MotionEvent.ACTION_HOVER_ENTER:
@@ -10685,6 +10772,110 @@
     }
 
     /**
+     * Handles scroll bar dragging by mouse input.
+     *
+     * @hide
+     * @param event The motion event.
+     *
+     * @return true if the event was handled as a scroll bar dragging, false otherwise.
+     */
+    protected boolean handleScrollBarDragging(MotionEvent event) {
+        if (mScrollCache == null) {
+            return false;
+        }
+        final float x = event.getX();
+        final float y = event.getY();
+        final int action = event.getAction();
+        if ((mScrollCache.mScrollBarDraggingState == ScrollabilityCache.NOT_DRAGGING
+                && action != MotionEvent.ACTION_DOWN)
+                    || !event.isFromSource(InputDevice.SOURCE_MOUSE)
+                    || !event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+            mScrollCache.mScrollBarDraggingState = ScrollabilityCache.NOT_DRAGGING;
+            return false;
+        }
+
+        switch (action) {
+            case MotionEvent.ACTION_MOVE:
+                if (mScrollCache.mScrollBarDraggingState == ScrollabilityCache.NOT_DRAGGING) {
+                    return false;
+                }
+                if (mScrollCache.mScrollBarDraggingState
+                        == ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR) {
+                    final Rect bounds = mScrollCache.mScrollBarBounds;
+                    getVerticalScrollBarBounds(bounds);
+                    final int range = computeVerticalScrollRange();
+                    final int offset = computeVerticalScrollOffset();
+                    final int extent = computeVerticalScrollExtent();
+
+                    final int thumbLength = ScrollBarUtils.getThumbLength(
+                            bounds.height(), bounds.width(), extent, range);
+                    final int thumbOffset = ScrollBarUtils.getThumbOffset(
+                            bounds.height(), thumbLength, extent, range, offset);
+
+                    final float diff = y - mScrollCache.mScrollBarDraggingPos;
+                    final float maxThumbOffset = bounds.height() - thumbLength;
+                    final float newThumbOffset =
+                            Math.min(Math.max(thumbOffset + diff, 0.0f), maxThumbOffset);
+                    final int height = getHeight();
+                    if (Math.round(newThumbOffset) != thumbOffset && maxThumbOffset > 0
+                            && height > 0 && extent > 0) {
+                        final int newY = Math.round((range - extent)
+                                / ((float)extent / height) * (newThumbOffset / maxThumbOffset));
+                        if (newY != getScrollY()) {
+                            mScrollCache.mScrollBarDraggingPos = y;
+                            setScrollY(newY);
+                        }
+                    }
+                    return true;
+                }
+                if (mScrollCache.mScrollBarDraggingState
+                        == ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR) {
+                    final Rect bounds = mScrollCache.mScrollBarBounds;
+                    getHorizontalScrollBarBounds(bounds);
+                    final int range = computeHorizontalScrollRange();
+                    final int offset = computeHorizontalScrollOffset();
+                    final int extent = computeHorizontalScrollExtent();
+
+                    final int thumbLength = ScrollBarUtils.getThumbLength(
+                            bounds.width(), bounds.height(), extent, range);
+                    final int thumbOffset = ScrollBarUtils.getThumbOffset(
+                            bounds.width(), thumbLength, extent, range, offset);
+
+                    final float diff = x - mScrollCache.mScrollBarDraggingPos;
+                    final float maxThumbOffset = bounds.width() - thumbLength;
+                    final float newThumbOffset =
+                            Math.min(Math.max(thumbOffset + diff, 0.0f), maxThumbOffset);
+                    final int width = getWidth();
+                    if (Math.round(newThumbOffset) != thumbOffset && maxThumbOffset > 0
+                            && width > 0 && extent > 0) {
+                        final int newX = Math.round((range - extent)
+                                / ((float)extent / width) * (newThumbOffset / maxThumbOffset));
+                        if (newX != getScrollX()) {
+                            mScrollCache.mScrollBarDraggingPos = x;
+                            setScrollX(newX);
+                        }
+                    }
+                    return true;
+                }
+            case MotionEvent.ACTION_DOWN:
+                if (isOnVerticalScrollbarThumb(x, y)) {
+                    mScrollCache.mScrollBarDraggingState =
+                            ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR;
+                    mScrollCache.mScrollBarDraggingPos = y;
+                    return true;
+                }
+                if (isOnHorizontalScrollbarThumb(x, y)) {
+                    mScrollCache.mScrollBarDraggingState =
+                            ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR;
+                    mScrollCache.mScrollBarDraggingPos = x;
+                    return true;
+                }
+        }
+        mScrollCache.mScrollBarDraggingState = ScrollabilityCache.NOT_DRAGGING;
+        return false;
+    }
+
+    /**
      * Implement this method to handle touch screen motion events.
      * <p>
      * If this method is used to detect click actions, it is recommended that
@@ -10717,7 +10908,6 @@
                     || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                     || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
         }
-
         if (mTouchDelegate != null) {
             if (mTouchDelegate.onTouchEvent(event)) {
                 return true;
@@ -14278,6 +14468,45 @@
         }
     }
 
+    private void getHorizontalScrollBarBounds(Rect bounds) {
+        final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
+        final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled()
+                && !isVerticalScrollBarHidden();
+        final int size = getHorizontalScrollbarHeight();
+        final int verticalScrollBarGap = drawVerticalScrollBar ?
+                getVerticalScrollbarWidth() : 0;
+        final int width = mRight - mLeft;
+        final int height = mBottom - mTop;
+        bounds.top = mScrollY + height - size - (mUserPaddingBottom & inside);
+        bounds.left = mScrollX + (mPaddingLeft & inside);
+        bounds.right = mScrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap;
+        bounds.bottom = bounds.top + size;
+    }
+
+    private void getVerticalScrollBarBounds(Rect bounds) {
+        final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
+        final int size = getVerticalScrollbarWidth();
+        int verticalScrollbarPosition = mVerticalScrollbarPosition;
+        if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) {
+            verticalScrollbarPosition = isLayoutRtl() ?
+                    SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT;
+        }
+        final int width = mRight - mLeft;
+        final int height = mBottom - mTop;
+        switch (verticalScrollbarPosition) {
+            default:
+            case SCROLLBAR_POSITION_RIGHT:
+                bounds.left = mScrollX + width - size - (mUserPaddingRight & inside);
+                break;
+            case SCROLLBAR_POSITION_LEFT:
+                bounds.left = mScrollX + (mUserPaddingLeft & inside);
+                break;
+        }
+        bounds.top = mScrollY + (mPaddingTop & inside);
+        bounds.right = bounds.left + size;
+        bounds.bottom = mScrollY + height - (mUserPaddingBottom & inside);
+    }
+
     /**
      * <p>Request the drawing of the horizontal and the vertical scrollbar. The
      * scrollbars are painted only if they have been awakened first.</p>
@@ -14325,80 +14554,36 @@
                 cache.scrollBar.mutate().setAlpha(255);
             }
 
-
-            final int viewFlags = mViewFlags;
-
-            final boolean drawHorizontalScrollBar =
-                (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL;
-            final boolean drawVerticalScrollBar =
-                (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL
-                && !isVerticalScrollBarHidden();
+            final boolean drawHorizontalScrollBar = isHorizontalScrollBarEnabled();
+            final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled()
+                    && !isVerticalScrollBarHidden();
 
             if (drawVerticalScrollBar || drawHorizontalScrollBar) {
-                final int width = mRight - mLeft;
-                final int height = mBottom - mTop;
-
                 final ScrollBarDrawable scrollBar = cache.scrollBar;
 
-                final int scrollX = mScrollX;
-                final int scrollY = mScrollY;
-                final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
-
-                int left;
-                int top;
-                int right;
-                int bottom;
-
                 if (drawHorizontalScrollBar) {
-                    int size = scrollBar.getSize(false);
-                    if (size <= 0) {
-                        size = cache.scrollBarSize;
-                    }
-
                     scrollBar.setParameters(computeHorizontalScrollRange(),
                                             computeHorizontalScrollOffset(),
                                             computeHorizontalScrollExtent(), false);
-                    final int verticalScrollBarGap = drawVerticalScrollBar ?
-                            getVerticalScrollbarWidth() : 0;
-                    top = scrollY + height - size - (mUserPaddingBottom & inside);
-                    left = scrollX + (mPaddingLeft & inside);
-                    right = scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap;
-                    bottom = top + size;
-                    onDrawHorizontalScrollBar(canvas, scrollBar, left, top, right, bottom);
+                    final Rect bounds = cache.mScrollBarBounds;
+                    getHorizontalScrollBarBounds(bounds);
+                    onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top,
+                            bounds.right, bounds.bottom);
                     if (invalidate) {
-                        invalidate(left, top, right, bottom);
+                        invalidate(bounds);
                     }
                 }
 
                 if (drawVerticalScrollBar) {
-                    int size = scrollBar.getSize(true);
-                    if (size <= 0) {
-                        size = cache.scrollBarSize;
-                    }
-
                     scrollBar.setParameters(computeVerticalScrollRange(),
                                             computeVerticalScrollOffset(),
                                             computeVerticalScrollExtent(), true);
-                    int verticalScrollbarPosition = mVerticalScrollbarPosition;
-                    if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) {
-                        verticalScrollbarPosition = isLayoutRtl() ?
-                                SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT;
-                    }
-                    switch (verticalScrollbarPosition) {
-                        default:
-                        case SCROLLBAR_POSITION_RIGHT:
-                            left = scrollX + width - size - (mUserPaddingRight & inside);
-                            break;
-                        case SCROLLBAR_POSITION_LEFT:
-                            left = scrollX + (mUserPaddingLeft & inside);
-                            break;
-                    }
-                    top = scrollY + (mPaddingTop & inside);
-                    right = left + size;
-                    bottom = scrollY + height - (mUserPaddingBottom & inside);
-                    onDrawVerticalScrollBar(canvas, scrollBar, left, top, right, bottom);
+                    final Rect bounds = cache.mScrollBarBounds;
+                    getVerticalScrollBarBounds(bounds);
+                    onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top,
+                            bounds.right, bounds.bottom);
                     if (invalidate) {
-                        invalidate(left, top, right, bottom);
+                        invalidate(bounds);
                     }
                 }
             }
@@ -21338,6 +21523,9 @@
      * @see PointerIcon
      */
     public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+        if (isDraggingScrollBar() || isOnScrollbarThumb(x, y)) {
+            return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_ARROW);
+        }
         return mPointerIcon;
     }
 
@@ -22609,6 +22797,15 @@
 
         private int mLastColor;
 
+        public final Rect mScrollBarBounds = new Rect();
+
+        public static final int NOT_DRAGGING = 0;
+        public static final int DRAGGING_VERTICAL_SCROLL_BAR = 1;
+        public static final int DRAGGING_HORIZONTAL_SCROLL_BAR = 2;
+        public int mScrollBarDraggingState = NOT_DRAGGING;
+
+        public float mScrollBarDraggingPos = 0;
+
         public ScrollabilityCache(ViewConfiguration configuration, View host) {
             fadingEdgeLength = configuration.getScaledFadingEdgeLength();
             scrollBarSize = configuration.getScaledScrollBarSize();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 27e2ea3..3fe6b8e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -59,6 +59,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+
 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
 
 /**
@@ -1651,6 +1652,9 @@
 
     @Override
     public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+        if (isOnScrollbarThumb(x, y) || isDraggingScrollBar()) {
+            return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_ARROW);
+        }
         // Check what the child under the pointer says about the pointer.
         final int childrenCount = mChildrenCount;
         if (childrenCount != 0) {
@@ -1974,7 +1978,7 @@
      * hover exit event in {@link #onHoverEvent} and then the hovered child will
      * receive a hover enter event.
      * </p><p>
-     * The default implementation always returns false.
+     * The default implementation handles mouse hover on the scroll bars.
      * </p>
      *
      * @param event The motion event that describes the hover.
@@ -1982,6 +1986,15 @@
      * and prevent its children from receiving it.
      */
     public boolean onInterceptHoverEvent(MotionEvent event) {
+        if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+            final int action = event.getAction();
+            final float x = event.getX();
+            final float y = event.getY();
+            if ((action == MotionEvent.ACTION_HOVER_MOVE
+                    || action == MotionEvent.ACTION_HOVER_ENTER) && isOnScrollbar(x, y)) {
+                return true;
+            }
+        }
         return false;
     }
 
@@ -2716,6 +2729,12 @@
      * messages will be delivered here.
      */
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
+                && ev.getAction() == MotionEvent.ACTION_DOWN
+                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
+                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
+            return true;
+        }
         return false;
     }
 
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index e9b123b5..1962be8 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -266,14 +266,14 @@
      *            intercept touch events.
      */
     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept);
-    
+
     /**
      * Called when a child of this group wants a particular rectangle to be
      * positioned onto the screen.  {@link ViewGroup}s overriding this can trust
      * that:
      * <ul>
      *   <li>child will be a direct child of this group</li>
-     *   <li>rectangle will be in the child's coordinates</li>
+     *   <li>rectangle will be in the child's content coordinates</li>
      * </ul>
      *
      * <p>{@link ViewGroup}s overriding this should uphold the contract:</p>
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0d6f362..5d41477 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6729,16 +6729,19 @@
 
     @Override
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+        if (rectangle == null) {
+            return scrollToRectOrFocus(null, immediate);
+        }
+        rectangle.offset(child.getLeft() - child.getScrollX(),
+                child.getTop() - child.getScrollY());
         final boolean scrolled = scrollToRectOrFocus(rectangle, immediate);
-        if (rectangle != null) {
-            mTempRect.set(rectangle);
-            mTempRect.offset(0, -mCurScrollY);
-            mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
-            try {
-                mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect);
-            } catch (RemoteException re) {
-                /* ignore */
-            }
+        mTempRect.set(rectangle);
+        mTempRect.offset(0, -mCurScrollY);
+        mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
+        try {
+            mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect);
+        } catch (RemoteException re) {
+            /* ignore */
         }
         return scrolled;
     }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 6c2c956..9561f08 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3701,6 +3701,13 @@
         }
     }
 
+    /** @hide */
+    @Override
+    protected boolean handleScrollBarDragging(MotionEvent event) {
+        // Doesn't support normal scroll bar dragging. Use FastScroller.
+        return false;
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (!isEnabled()) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3cc1d69..617d3dd 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1083,6 +1083,20 @@
         return true;
     }
 
+    private void startDragAndDrop() {
+        final int start = mTextView.getSelectionStart();
+        final int end = mTextView.getSelectionEnd();
+        CharSequence selectedText = mTextView.getTransformedText(start, end);
+        ClipData data = ClipData.newPlainText(null, selectedText);
+        DragLocalState localState = new DragLocalState(mTextView, start, end);
+        mTextView.startDragAndDrop(data, getTextThumbnailBuilder(selectedText), localState,
+                View.DRAG_FLAG_GLOBAL);
+        stopTextActionMode();
+        if (hasSelectionController()) {
+            getSelectionController().resetTouchOffsets();
+        }
+    }
+
     public boolean performLongClick(boolean handled) {
         // Long press in empty space moves cursor and starts the insertion action mode.
         if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY) &&
@@ -1098,15 +1112,7 @@
 
         if (!handled && mTextActionMode != null) {
             if (touchPositionIsInSelection()) {
-                // Start a drag
-                final int start = mTextView.getSelectionStart();
-                final int end = mTextView.getSelectionEnd();
-                CharSequence selectedText = mTextView.getTransformedText(start, end);
-                ClipData data = ClipData.newPlainText(null, selectedText);
-                DragLocalState localState = new DragLocalState(mTextView, start, end);
-                mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState,
-                        View.DRAG_FLAG_GLOBAL);
-                stopTextActionMode();
+                startDragAndDrop();
             } else {
                 stopTextActionMode();
                 selectCurrentWordAndStartDrag();
@@ -4939,6 +4945,14 @@
 
                     if (isMouse && !isDragAcceleratorActive()) {
                         final int offset = mTextView.getOffsetForPosition(eventX, eventY);
+                        if (mTextView.hasSelection()
+                                && (!mHaventMovedEnoughToStartDrag || mStartOffset != offset)
+                                && offset >= mTextView.getSelectionStart()
+                                && offset <= mTextView.getSelectionEnd()) {
+                            startDragAndDrop();
+                            break;
+                        }
+
                         if (mStartOffset != offset) {
                             // Start character based drag accelerator.
                             if (mTextActionMode != null) {
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index ebc7eb3..f16fdd6 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -454,6 +454,10 @@
             return true;
         }
 
+        if (super.onInterceptTouchEvent(ev)) {
+            return true;
+        }
+
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_MOVE: {
                 /*
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 91d6232..8880217 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import com.android.internal.widget.ScrollBarUtils;
+
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
@@ -135,23 +137,15 @@
         }
 
         if (drawThumb) {
-            final int size = vertical ? r.height() : r.width();
+            final int scrollBarLength = vertical ? r.height() : r.width();
             final int thickness = vertical ? r.width() : r.height();
-            final int minLength = thickness * 2;
+            final int thumbLength =
+                    ScrollBarUtils.getThumbLength(scrollBarLength, thickness, extent, range);
+            final int thumbOffset =
+                    ScrollBarUtils.getThumbOffset(scrollBarLength, thumbLength, extent, range,
+                            mOffset);
 
-            // Avoid the tiny thumb.
-            int length = Math.round((float) size * extent / range);
-            if (length < minLength) {
-                length = minLength;
-            }
-
-            // Avoid the too-big thumb.
-            int offset = Math.round((float) (size - length) * mOffset / (range - extent));
-            if (offset > size - length) {
-                offset = size - length;
-            }
-
-            drawThumb(canvas, r, offset, length, vertical);
+            drawThumb(canvas, r, thumbOffset, thumbLength, vertical);
         }
     }
 
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 78b931d..3f7a07b 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -489,6 +489,10 @@
             return true;
         }
 
+        if (super.onInterceptTouchEvent(ev)) {
+            return true;
+        }
+
         /*
          * Don't try to intercept touch if we can't scroll anyway.
          */
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 5b40bc0..a572486 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -237,6 +237,22 @@
         return mMiddleTarget;
     }
 
+    public SnapTarget getNextTarget(SnapTarget snapTarget) {
+        int index = mTargets.indexOf(snapTarget);
+        if (index != -1 && index < mTargets.size() - 1) {
+            return mTargets.get(index + 1);
+        }
+        return snapTarget;
+    }
+
+    public SnapTarget getPreviousTarget(SnapTarget snapTarget) {
+        int index = mTargets.indexOf(snapTarget);
+        if (index != -1 && index > 0) {
+            return mTargets.get(index - 1);
+        }
+        return snapTarget;
+    }
+
     /**
      * Represents a snap target for the divider.
      */
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 381e71f..2f26e92 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -42,6 +42,23 @@
      * @return the string reference that was validated
      * @throws IllegalArgumentException if {@code string} is empty
      */
+    public static @NonNull String checkStringNotEmpty(final String string) {
+        if (TextUtils.isEmpty(string)) {
+            throw new IllegalArgumentException();
+        }
+        return string;
+    }
+
+    /**
+     * Ensures that an string reference passed as a parameter to the calling
+     * method is not empty.
+     *
+     * @param string an string reference
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @return the string reference that was validated
+     * @throws IllegalArgumentException if {@code string} is empty
+     */
     public static @NonNull String checkStringNotEmpty(final String string,
             final Object errorMessage) {
         if (TextUtils.isEmpty(string)) {
@@ -301,8 +318,8 @@
      *
      * @throws NullPointerException if the {@code value} or any of its elements were {@code null}
      */
-    public static <T> Collection<T> checkCollectionElementsNotNull(final Collection<T> value,
-            final String valueName) {
+    public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull(
+            final C value, final String valueName) {
         if (value == null) {
             throw new NullPointerException(valueName + " must not be null");
         }
diff --git a/core/java/com/android/internal/widget/ScrollBarUtils.java b/core/java/com/android/internal/widget/ScrollBarUtils.java
new file mode 100644
index 0000000..0ae9f74
--- /dev/null
+++ b/core/java/com/android/internal/widget/ScrollBarUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+public class ScrollBarUtils {
+
+    public static int getThumbLength(int size, int thickness, int extent, int range) {
+        // Avoid the tiny thumb.
+        final int minLength = thickness * 2;
+        int length = Math.round((float) size * extent / range);
+        if (length < minLength) {
+            length = minLength;
+        }
+        return length;
+    }
+
+    public static int getThumbOffset(int size, int thumbLength, int extent, int range, int offset) {
+        // Avoid the too-big thumb.
+        int thumbOffset = Math.round((float) (size - thumbLength) * offset / (range - extent));
+        if (thumbOffset > size - thumbLength) {
+            thumbOffset = size - thumbLength;
+        }
+        return thumbOffset;
+    }
+}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 34877e0..35b5016 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -86,20 +86,20 @@
 }
 
 static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
-    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
     return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
 }
 
 static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
                       jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
     Paint* paint  = reinterpret_cast<Paint*>(paintHandle);
-    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
     return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
 }
 
 static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
                            jfloat r, jfloat b, jint alpha, jint flagsHandle) {
-    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
 }
 
@@ -351,7 +351,7 @@
     if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) {
         canvas->drawNinePatch(skiaBitmap, *chunk, left, top, right, bottom, paint);
     } else {
-        canvas->save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas->save(SaveFlags::MatrixClip);
 
         SkScalar scale = dstDensity / (float)srcDensity;
         canvas->translate(left, top);
@@ -390,7 +390,7 @@
             canvas->drawBitmap(bitmap, left, top, paint);
         }
     } else {
-        canvas->save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas->save(SaveFlags::MatrixClip);
         SkScalar scale = canvasDensity / (float)bitmapDensity;
         canvas->translate(left, top);
         canvas->scale(scale, scale);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3d4b8b4..56c3fc8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -383,6 +383,7 @@
     <protected-broadcast android:name="android.security.STORAGE_CHANGED" />
     <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" />
     <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" />
+    <protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
     <protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
 
     <protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
@@ -1207,7 +1208,8 @@
     <eat-comment />
 
     <!-- Allows access to the list of accounts in the Accounts Service.
-        <p>Protection level: normal
+         <p>Protection level: dangerous
+         @deprecated Not operative for apps apps with targetSdkVersion >= 24.
     -->
     <permission android:name="android.permission.GET_ACCOUNTS"
         android:permissionGroup="android.permission-group.CONTACTS"
diff --git a/core/res/res/drawable-xhdpi/pointer_alias_large.png b/core/res/res/drawable-xhdpi/pointer_alias_large.png
new file mode 100644
index 0000000..813cd2a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_alias_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png b/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png
new file mode 100644
index 0000000..f101077
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_all_scroll_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_arrow_large.png
new file mode 100644
index 0000000..0f9165b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_cell_large.png b/core/res/res/drawable-xhdpi/pointer_cell_large.png
new file mode 100644
index 0000000..b41f855
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_cell_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_context_menu_large.png b/core/res/res/drawable-xhdpi/pointer_context_menu_large.png
new file mode 100644
index 0000000..6264e0b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_context_menu_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_copy_large.png b/core/res/res/drawable-xhdpi/pointer_copy_large.png
new file mode 100644
index 0000000..5e7f375
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_copy_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_crosshair_large.png b/core/res/res/drawable-xhdpi/pointer_crosshair_large.png
new file mode 100644
index 0000000..d5f4502
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_crosshair_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grab_large.png b/core/res/res/drawable-xhdpi/pointer_grab_large.png
new file mode 100644
index 0000000..bf99b79
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_grab_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_grabbing_large.png b/core/res/res/drawable-xhdpi/pointer_grabbing_large.png
new file mode 100644
index 0000000..e576bcd
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_grabbing_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_hand_large.png b/core/res/res/drawable-xhdpi/pointer_hand_large.png
new file mode 100644
index 0000000..76fa09b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_hand_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_help_large.png b/core/res/res/drawable-xhdpi/pointer_help_large.png
new file mode 100644
index 0000000..a45f039
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_help_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png
new file mode 100644
index 0000000..9b2a312
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_horizontal_double_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_nodrop_large.png b/core/res/res/drawable-xhdpi/pointer_nodrop_large.png
new file mode 100644
index 0000000..f140532
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_nodrop_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_text_large.png b/core/res/res/drawable-xhdpi/pointer_text_large.png
new file mode 100644
index 0000000..56be154
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_text_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png
new file mode 100644
index 0000000..9e6a8b6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_top_left_diagonal_double_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png
new file mode 100644
index 0000000..2631094
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_top_right_diagonal_double_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png
new file mode 100644
index 0000000..5ec65e0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_vertical_double_arrow_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_vertical_text_large.png b/core/res/res/drawable-xhdpi/pointer_vertical_text_large.png
new file mode 100644
index 0000000..26c46de
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_vertical_text_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_zoom_in_large.png b/core/res/res/drawable-xhdpi/pointer_zoom_in_large.png
new file mode 100644
index 0000000..c2b845f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_zoom_in_large.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/pointer_zoom_out_large.png b/core/res/res/drawable-xhdpi/pointer_zoom_out_large.png
new file mode 100644
index 0000000..148ed25
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/pointer_zoom_out_large.png
Binary files differ
diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml
new file mode 100644
index 0000000..aaa2dbc
--- /dev/null
+++ b/core/res/res/layout/app_error_dialog.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingTop="@dimen/dialog_list_padding_vertical_material"
+        android:paddingBottom="@dimen/dialog_list_padding_vertical_material"
+>
+
+
+    <TextView
+            android:id="@+id/aerr_restart"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/aerr_restart"
+            style="@style/aerr_list_item"
+    />
+
+    <TextView
+            android:id="@+id/aerr_reset"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/aerr_reset"
+            style="@style/aerr_list_item"
+    />
+
+    <TextView
+            android:id="@+id/aerr_report"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/aerr_report"
+            style="@style/aerr_list_item"
+    />
+
+    <TextView
+            android:id="@+id/aerr_close"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/aerr_close"
+            style="@style/aerr_list_item"
+    />
+
+    <TextView
+            android:id="@+id/aerr_mute"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/aerr_mute"
+            style="@style/aerr_list_item"
+    />
+
+
+</LinearLayout>
diff --git a/core/res/res/layout/app_error_dialog_dont_show_again.xml b/core/res/res/layout/app_error_dialog_dont_show_again.xml
deleted file mode 100644
index ba79ecd..0000000
--- a/core/res/res/layout/app_error_dialog_dont_show_again.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingStart="14dp"
-        android:paddingEnd="10dp"
-        android:gravity="center_vertical"
-        >
-    <CheckBox
-            android:id="@+id/checkbox"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            />
-    <TextView
-            android:id="@+id/text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="8dp"
-            android:layout_marginBottom="8dp"
-            />
-
-</LinearLayout>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 28c76bb..a276854 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2588,16 +2588,27 @@
     <!-- Text to display when there are no activities found to display in the
          activity chooser. See the "Select an action" title. -->
     <string name="noApplications">No apps can perform this action.</string>
-    <!-- Title of the alert when an application has crashed. -->
-    <string name="aerr_title"></string>
     <!-- Text of the alert that is displayed when an application has crashed. -->
-    <string name="aerr_application">Unfortunately, <xliff:g id="application">%1$s</xliff:g> has stopped.</string>
-    <!-- Text of the alert that is displayed when an application has crashed. -->
-    <string name="aerr_process">Unfortunately, the process <xliff:g id="process">%1$s</xliff:g> has
-        stopped.</string>
-    <!-- Text of the alert that is displayed when an application has crashed. -->
-    <string name="aerr_process_silence">Silence crashes from <xliff:g id="process">%1$s</xliff:g>
-        until reboot.</string>
+    <string name="aerr_application"><xliff:g id="application">%1$s</xliff:g> has stopped</string>
+    <!-- Text of the alert that is displayed when a process has crashed. -->
+    <string name="aerr_process"><xliff:g id="process">%1$s</xliff:g> has
+        stopped</string>
+    <!-- Text of the alert that is displayed when an application has crashed repeatedly. -->
+    <string name="aerr_application_repeated"><xliff:g id="application">%1$s</xliff:g> is repeatedly stopping</string>
+    <!-- Text of the alert that is displayed when a process has crashed repeatedly. -->
+    <string name="aerr_process_repeated"><xliff:g id="process">%1$s</xliff:g> is
+        repeatedly stopping</string>
+    <!-- Button that restarts a crashed application -->
+    <string name="aerr_restart">Restart app</string>
+    <!-- Button that clears cache and restarts a crashed application -->
+    <string name="aerr_reset">Reset and restart app</string>
+    <!-- Button that sends feedback about a crashed application -->
+    <string name="aerr_report">Send feedback</string>
+    <!-- Button that closes a crashed application -->
+    <string name="aerr_close">Close</string>
+    <!-- Button that mutes further crashes of the crashed application-->
+    <string name="aerr_mute">Mute</string>
+
     <!-- Title of the alert when an application is not responding. -->
     <string name="anr_title"></string>
     <!-- Text of the alert that is displayed when an application is not responding. -->
@@ -4174,4 +4185,9 @@
     <string name="suspended_package_title">%1$s disabled</string>
     <!-- Message for dialog displayed when a packge is suspended by device admin. [CHAR LIMIT=NONE] -->
     <string name="suspended_package_message">Disabled by %1$s administrator. Contact them to learn more.</string>
+
+    <!-- Notification title shown when new SMS/MMS is received while the device is locked [CHAR LIMIT=NONE] -->
+    <string name="new_sms_notification_title">You have new messages</string>
+    <!-- Notification content shown when new SMS/MMS is received while the device is locked [CHAR LIMIT=NONE] -->
+    <string name="new_sms_notification_content">Open SMS app to view</string>
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index d5349b2..b660277 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1397,6 +1397,16 @@
         <item name="pointerIconGrabbing">@drawable/pointer_grabbing_large_icon</item>
     </style>
 
+    <!-- @hide -->
+    <style name="aerr_list_item" parent="Widget.Material.Light.TextView">
+        <item name="minHeight">?attr/listPreferredItemHeightSmall</item>
+        <item name="textAppearance">?attr/textAppearanceListItemSmall</item>
+        <item name="textColor">?attr/textColorAlertDialogListItem</item>
+        <item name="gravity">center_vertical</item>
+        <item name="paddingStart">?attr/listPreferredItemPaddingStart</item>
+        <item name="paddingEnd">?attr/listPreferredItemPaddingEnd</item>
+    </style>
+
     <!-- Wifi dialog styles -->
     <!-- @hide -->
     <style name="wifi_item">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 57ff243..e59c935 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1763,7 +1763,7 @@
   <java-symbol type="layout" name="launch_warning" />
   <java-symbol type="layout" name="safe_mode" />
   <java-symbol type="layout" name="simple_list_item_2_single_choice" />
-  <java-symbol type="layout" name="app_error_dialog_dont_show_again" />
+  <java-symbol type="layout" name="app_error_dialog" />
   <java-symbol type="plurals" name="wifi_available" />
   <java-symbol type="plurals" name="wifi_available_detailed" />
   <java-symbol type="string" name="accessibility_binding_label" />
@@ -1777,8 +1777,8 @@
   <java-symbol type="string" name="remote_bugreport_progress_notification_message_can_cancel" />
   <java-symbol type="string" name="aerr_application" />
   <java-symbol type="string" name="aerr_process" />
-  <java-symbol type="string" name="aerr_process_silence" />
-  <java-symbol type="string" name="aerr_title" />
+  <java-symbol type="string" name="aerr_application_repeated" />
+  <java-symbol type="string" name="aerr_process_repeated" />
   <java-symbol type="string" name="android_upgrading_fstrim" />
   <java-symbol type="string" name="android_upgrading_apk" />
   <java-symbol type="string" name="android_upgrading_complete" />
@@ -2429,6 +2429,12 @@
   <java-symbol type="id" name="work_widget_app_icon" />
   <java-symbol type="drawable" name="work_widget_mask_view_background" />
 
+  <java-symbol type="id" name="aerr_report" />
+  <java-symbol type="id" name="aerr_reset" />
+  <java-symbol type="id" name="aerr_restart" />
+  <java-symbol type="id" name="aerr_close" />
+  <java-symbol type="id" name="aerr_mute" />
+
   <!-- Framework-private Material.DayNight styles. -->
   <java-symbol type="style" name="Theme.Material.DayNight" />
   <java-symbol type="style" name="Theme.Material.DayNight.DarkActionBar" />
@@ -2493,4 +2499,8 @@
   <java-symbol type="string" name="work_mode_turn_on" />
   <java-symbol type="string" name="suspended_package_title" />
   <java-symbol type="string" name="suspended_package_message" />
+
+  <!-- New SMS notification while phone is locked. -->
+  <java-symbol type="string" name="new_sms_notification_title" />
+  <java-symbol type="string" name="new_sms_notification_content" />
 </resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 5a08887..cf7978c 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -214,7 +214,7 @@
 
     <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
          visual voicemail code for T-Mobile: 122 -->
-    <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327|654)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" free="122|87902" />
+    <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:567|578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" free="122|87902" />
 
     <!-- Vietnam -->
     <shortcode country="vn" free="5001" />
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index ee8921e..eb055de 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -32,7 +32,8 @@
     littlemock \
     android-support-test \
     mockito-target \
-    espresso-core
+    espresso-core \
+    ub-uiautomator
 LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index c0453f8..bfa2b10 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -53,6 +53,7 @@
     <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
     <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.READ_LOGS"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
@@ -1294,6 +1295,26 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.print.PrintTestActivity"/>
+
+        <service
+            android:name="android.print.mockservice.MockPrintService"
+            android:permission="android.permission.BIND_PRINT_SERVICE">
+            <intent-filter>
+                <action android:name="android.printservice.PrintService" />
+            </intent-filter>
+            <meta-data
+               android:name="android.printservice"
+               android:resource="@xml/printservice">
+            </meta-data>
+        </service>
+
+        <activity
+            android:name="android.print.mockservice.SettingsActivity"
+            android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+            android:exported="true">
+        </activity>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/res/xml/printservice.xml b/core/tests/coretests/res/xml/printservice.xml
new file mode 100644
index 0000000..abbebda
--- /dev/null
+++ b/core/tests/coretests/res/xml/printservice.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<print-service  xmlns:android="http://schemas.android.com/apk/res/android"
+     android:settingsActivity="android.print.mockservice.SettingsActivity"/>
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
new file mode 100644
index 0000000..19ce44a
--- /dev/null
+++ b/core/tests/coretests/src/android/print/BasePrintTest.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.mockservice.PrintServiceCallbacks;
+import android.print.mockservice.PrinterDiscoverySessionCallbacks;
+import android.print.mockservice.StubbablePrinterDiscoverySession;
+import android.printservice.CustomPrinterIconCallback;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.test.InstrumentationTestCase;
+import android.util.DisplayMetrics;
+import android.util.LocaleList;
+
+import org.mockito.stubbing.Answer;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This is the base class for print tests.
+ */
+public abstract class BasePrintTest extends InstrumentationTestCase {
+
+    private static final long OPERATION_TIMEOUT = 30000;
+    private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
+    private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
+    private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s";
+    private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable ";
+    private static final String COMMAND_PREFIX_DISABLE_IME = "ime disable ";
+    private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
+
+    private PrintTestActivity mActivity;
+    private android.print.PrintJob mPrintJob;
+
+    private LocaleList mOldLocale;
+
+    private CallCounter mStartCallCounter;
+    private CallCounter mStartSessionCallCounter;
+
+    private String[] mEnabledImes;
+
+    private String[] getEnabledImes() throws IOException {
+        List<String> imeList = new ArrayList<>();
+
+        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+                .executeShellCommand(COMMAND_LIST_ENABLED_IME_COMPONENTS);
+        try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) {
+
+            String line;
+            while ((line = reader.readLine()) != null) {
+                imeList.add(line);
+            }
+        }
+
+        String[] imeArray = new String[imeList.size()];
+        imeList.toArray(imeArray);
+
+        return imeArray;
+    }
+
+    private void disableImes() throws Exception {
+        mEnabledImes = getEnabledImes();
+        for (String ime : mEnabledImes) {
+            String disableImeCommand = COMMAND_PREFIX_DISABLE_IME + ime;
+            runShellCommand(getInstrumentation(), disableImeCommand);
+        }
+    }
+
+    private void enableImes() throws Exception {
+        for (String ime : mEnabledImes) {
+            String enableImeCommand = COMMAND_PREFIX_ENABLE_IME + ime;
+            runShellCommand(getInstrumentation(), enableImeCommand);
+        }
+        mEnabledImes = null;
+    }
+
+    @Override
+    protected void runTest() throws Throwable {
+        // Do nothing if the device does not support printing.
+        if (supportsPrinting()) {
+            super.runTest();
+        }
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        if (!supportsPrinting()) {
+            return;
+        }
+
+        // Make sure we start with a clean slate.
+        clearPrintSpoolerData();
+        disableImes();
+
+        // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
+        // Dexmaker is used by mockito.
+        System.setProperty("dexmaker.dexcache", getInstrumentation()
+                .getTargetContext().getCacheDir().getPath());
+
+        // Set to US locale.
+        Resources resources = getInstrumentation().getTargetContext().getResources();
+        Configuration oldConfiguration = resources.getConfiguration();
+        if (!oldConfiguration.getLocales().getPrimary().equals(Locale.US)) {
+            mOldLocale = oldConfiguration.getLocales();
+            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+            Configuration newConfiguration = new Configuration(oldConfiguration);
+            newConfiguration.setLocale(Locale.US);
+            resources.updateConfiguration(newConfiguration, displayMetrics);
+        }
+
+        // Initialize the latches.
+        mStartCallCounter = new CallCounter();
+        mStartSessionCallCounter = new CallCounter();
+
+        // Create the activity for the right locale.
+        createActivity();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        if (!supportsPrinting()) {
+            return;
+        }
+
+        // Done with the activity.
+        getActivity().finish();
+        enableImes();
+
+        // Restore the locale if needed.
+        if (mOldLocale != null) {
+            Resources resources = getInstrumentation().getTargetContext().getResources();
+            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+            Configuration newConfiguration = new Configuration(resources.getConfiguration());
+            newConfiguration.setLocales(mOldLocale);
+            mOldLocale = null;
+            resources.updateConfiguration(newConfiguration, displayMetrics);
+        }
+
+        // Make sure the spooler is cleaned, this also un-approves all services
+        clearPrintSpoolerData();
+
+        super.tearDown();
+    }
+
+    protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
+            final PrintAttributes attributes) {
+        // Initiate printing as if coming from the app.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                PrintManager printManager = (PrintManager) getActivity()
+                        .getSystemService(Context.PRINT_SERVICE);
+                mPrintJob = printManager.print("Print job", adapter, attributes);
+            }
+        });
+
+        return mPrintJob;
+    }
+
+    protected void onStartCalled() {
+        mStartCallCounter.call();
+    }
+
+    protected void onPrinterDiscoverySessionStartCalled() {
+        mStartSessionCallCounter.call();
+    }
+
+    protected void waitForPrinterDiscoverySessionStartCallbackCalled() {
+        waitForCallbackCallCount(mStartSessionCallCounter, 1,
+                "Did not get expected call to onStartPrinterDiscoverySession.");
+    }
+
+    protected void waitForStartAdapterCallbackCalled() {
+        waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start.");
+    }
+
+    private void waitForCallbackCallCount(CallCounter counter, int count, String message) {
+        try {
+            counter.waitForCount(count, OPERATION_TIMEOUT);
+        } catch (TimeoutException te) {
+            fail(message);
+        }
+    }
+
+    protected PrintTestActivity getActivity() {
+        return mActivity;
+    }
+
+    protected void createActivity() {
+        mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
+                PrintTestActivity.class, null);
+    }
+
+    public static String runShellCommand(Instrumentation instrumentation, String cmd)
+            throws IOException {
+        ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd);
+        byte[] buf = new byte[512];
+        int bytesRead;
+        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+        StringBuffer stdout = new StringBuffer();
+        while ((bytesRead = fis.read(buf)) != -1) {
+            stdout.append(new String(buf, 0, bytesRead));
+        }
+        fis.close();
+        return stdout.toString();
+    }
+
+    protected void clearPrintSpoolerData() throws Exception {
+        assertTrue("failed to clear print spooler data",
+                runShellCommand(getInstrumentation(), String.format(
+                        "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
+                                .contains(PM_CLEAR_SUCCESS_OUTPUT));
+    }
+
+    @SuppressWarnings("unchecked")
+    protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
+            Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
+            Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
+            Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
+            Answer<Void> onDestroy) {
+        PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
+
+        doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
+        when(callbacks.getSession()).thenCallRealMethod();
+
+        if (onStartPrinterDiscovery != null) {
+            doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
+                    any(List.class));
+        }
+        if (onStopPrinterDiscovery != null) {
+            doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
+        }
+        if (onValidatePrinters != null) {
+            doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
+                    any(List.class));
+        }
+        if (onStartPrinterStateTracking != null) {
+            doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
+                    any(PrinterId.class));
+        }
+        if (onRequestCustomPrinterIcon != null) {
+            doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
+                    any(PrinterId.class), any(CustomPrinterIconCallback.class));
+        }
+        if (onStopPrinterStateTracking != null) {
+            doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
+                    any(PrinterId.class));
+        }
+        if (onDestroy != null) {
+            doAnswer(onDestroy).when(callbacks).onDestroy();
+        }
+
+        return callbacks;
+    }
+
+    protected PrintServiceCallbacks createMockPrintServiceCallbacks(
+            Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
+            Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
+        final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
+
+        doCallRealMethod().when(service).setService(any(PrintService.class));
+        when(service.getService()).thenCallRealMethod();
+
+        if (onCreatePrinterDiscoverySessionCallbacks != null) {
+            doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
+                    .onCreatePrinterDiscoverySessionCallbacks();
+        }
+        if (onPrintJobQueued != null) {
+            doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
+        }
+        if (onRequestCancelPrintJob != null) {
+            doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
+                    any(PrintJob.class));
+        }
+
+        return service;
+    }
+
+    protected final class CallCounter {
+        private final Object mLock = new Object();
+
+        private int mCallCount;
+
+        public void call() {
+            synchronized (mLock) {
+                mCallCount++;
+                mLock.notifyAll();
+            }
+        }
+
+        public int getCallCount() {
+            synchronized (mLock) {
+                return mCallCount;
+            }
+        }
+
+        public void waitForCount(int count, long timeoutMillis) throws TimeoutException {
+            synchronized (mLock) {
+                final long startTimeMillis = SystemClock.uptimeMillis();
+                while (mCallCount < count) {
+                    try {
+                        final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                        final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
+                        if (remainingTimeMillis <= 0) {
+                            throw new TimeoutException();
+                        }
+                        mLock.wait(timeoutMillis);
+                    } catch (InterruptedException ie) {
+                        /* ignore */
+                    }
+                }
+            }
+        }
+    }
+
+    protected boolean supportsPrinting() {
+        return getInstrumentation().getContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_PRINTING);
+    }
+}
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
new file mode 100644
index 0000000..5179b42
--- /dev/null
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.print.IPrintDocumentAdapter;
+import android.print.IPrintJobStateChangeListener;
+import android.print.IPrintManager;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintJob;
+import android.print.PrintJobId;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterDiscoverySession;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.PrintServiceInfo;
+
+import android.print.mockservice.MockPrintService;
+import android.print.mockservice.PrintServiceCallbacks;
+import android.print.mockservice.PrinterDiscoverySessionCallbacks;
+import android.print.mockservice.StubbablePrinterDiscoverySession;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * tests feeding all possible parameters to the IPrintManager Binder.
+ */
+public class IPrintManagerParametersTest extends BasePrintTest {
+
+    private final int BAD_APP_ID = 0xffffffff;
+
+    private final int mAppId;
+    private final int mUserId;
+    private final PrintJobId mBadPrintJobId;
+
+    private PrintJob mGoodPrintJob;
+    private PrinterId mBadPrinterId;
+    private PrinterId mGoodPrinterId;
+    private ComponentName mGoodComponentName;
+    private ComponentName mBadComponentName;
+
+    private IPrintManager mIPrintManager;
+
+    /**
+     * Create a new IPrintManagerParametersTest and setup basic fields.
+     */
+    public IPrintManagerParametersTest() {
+        super();
+
+        mAppId = UserHandle.getAppId(Process.myUid());
+        mUserId = UserHandle.myUserId();
+        mBadPrintJobId = new PrintJobId();
+        mBadComponentName = new ComponentName("bad", "bad");
+    }
+
+    /**
+     * Create a mock PrintDocumentAdapter.
+     *
+     * @return The adapter
+     */
+    private @NonNull PrintDocumentAdapter createMockAdapter() {
+        return new PrintDocumentAdapter() {
+            @Override
+            public void onStart() {
+                onStartCalled();
+            }
+
+            @Override
+            public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+                    CancellationSignal cancellationSignal, LayoutResultCallback callback,
+                    Bundle extras) {
+            }
+
+            @Override
+            public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
+                    CancellationSignal cancellationSignal, WriteResultCallback callback) {
+            }
+        };
+    }
+
+    /**
+     * Create mock print service callbacks.
+     *
+     * @return the callbacks
+     */
+    private PrintServiceCallbacks createMockCallbacks() {
+        return createMockPrintServiceCallbacks(
+                new Answer<PrinterDiscoverySessionCallbacks>() {
+                    @Override
+                    public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                        return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
+                            @Override
+                            public Void answer(InvocationOnMock invocation) {
+                                // Get the session.
+                                StubbablePrinterDiscoverySession session =
+                                        ((PrinterDiscoverySessionCallbacks) invocation
+                                        .getMock()).getSession();
+
+                                if (session.getPrinters().isEmpty()) {
+                                    final String PRINTER_NAME = "good printer";
+                                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+                                    // Add the printer.
+                                    mGoodPrinterId = session.getService()
+                                            .generatePrinterId(PRINTER_NAME);
+
+                                    PrinterCapabilitiesInfo capabilities =
+                                            new PrinterCapabilitiesInfo.Builder(mGoodPrinterId)
+                                                    .setMinMargins(
+                                                            new Margins(200, 200, 200, 200))
+                                                    .addMediaSize(MediaSize.ISO_A4, true)
+                                                    .addResolution(new Resolution("300x300",
+                                                            "300x300", 300, 300),
+                                                            true)
+                                                    .setColorModes(
+                                                            PrintAttributes.COLOR_MODE_COLOR,
+                                                            PrintAttributes.COLOR_MODE_COLOR)
+                                                    .build();
+
+                                    PrinterInfo printer = new PrinterInfo.Builder(
+                                            mGoodPrinterId,
+                                            PRINTER_NAME,
+                                            PrinterInfo.STATUS_IDLE)
+                                                    .setCapabilities(capabilities)
+                                                    .build();
+                                    printers.add(printer);
+
+                                    session.addPrinters(printers);
+                                }
+                                onPrinterDiscoverySessionStartCalled();
+                                return null;
+                            }
+                        }, null, null, null, null, null, null);
+                    }
+                },
+                null, null);
+    }
+
+    /**
+     * Create a IPrintJobStateChangeListener object.
+     *
+     * @return the object
+     * @throws Exception if the object could not be created.
+     */
+    private IPrintJobStateChangeListener createMockIPrintJobStateChangeListener() throws Exception {
+        return new PrintManager.PrintJobStateChangeListenerWrapper(null,
+                new Handler(Looper.getMainLooper()));
+    }
+
+    /**
+     * Create a IPrinterDiscoveryObserver object.
+     *
+     * @return the object
+     * @throws Exception if the object could not be created.
+     */
+    private IPrinterDiscoveryObserver createMockIPrinterDiscoveryObserver() throws Exception {
+        return new PrinterDiscoverySession.PrinterDiscoveryObserver(null);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        MockPrintService.setCallbacks(createMockCallbacks());
+
+        mGoodComponentName = getActivity().getComponentName();
+
+        mGoodPrintJob = print(createMockAdapter(), null);
+
+        mIPrintManager = IPrintManager.Stub
+                .asInterface(ServiceManager.getService(Context.PRINT_SERVICE));
+
+        // Generate dummy printerId which is a valid PrinterId object, but does not correspond to a
+        // printer
+        mBadPrinterId = new PrinterId(mGoodComponentName, "dummy printer");
+
+        // Wait for PrintActivity to be ready
+        waitForStartAdapterCallbackCalled();
+
+        // Wait for printer discovery session to be ready
+        waitForPrinterDiscoverySessionStartCallbackCalled();
+    }
+
+    /**
+     * {@link Runnable} that can throw and {@link Exception}
+     */
+    private interface Invokable {
+        /**
+         * Execute the {@link Invokable}
+         *
+         * @throws Exception
+         */
+        public void run() throws Exception;
+    }
+
+    /**
+     * Assert that the invokable throws an expectedException
+     *
+     * @param invokable The {@link Invokable} to run
+     * @param expectedClass The {@link Exception} that is supposed to be thrown
+     */
+    public void assertException(Invokable invokable, Class<? extends Exception> expectedClass)
+            throws Exception {
+        try {
+            invokable.run();
+        } catch (Exception e) {
+            if (e.getClass().isAssignableFrom(expectedClass)) {
+                return;
+            } else {
+                throw new AssertionError("Expected: " + expectedClass.getName() + ", got: "
+                                + e.getClass().getName());
+            }
+        }
+
+        throw new AssertionError("No exception thrown");
+    }
+
+    /**
+     * test IPrintManager.getPrintJobInfo
+     */
+    public void testGetPrintJobInfo() throws Exception {
+        assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(),
+                        mAppId, mUserId).getId());
+        assertEquals(null, mIPrintManager.getPrintJobInfo(mBadPrintJobId, mAppId, mUserId));
+        assertEquals(null, mIPrintManager.getPrintJobInfo(null, mAppId, mUserId));
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(), BAD_APP_ID, mUserId);
+            }
+        }, SecurityException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.getPrintJobInfos
+     */
+    public void testGetPrintJobInfos() throws Exception {
+        List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId);
+
+        boolean foundPrintJob = false;
+        for (PrintJobInfo info : infos) {
+            if (info.getId().equals(mGoodPrintJob.getId())) {
+                assertEquals(PrintJobInfo.STATE_CREATED, info.getState());
+                foundPrintJob = true;
+            }
+        }
+        assertTrue(foundPrintJob);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.getPrintJobInfos(BAD_APP_ID, mUserId);
+            }
+        }, SecurityException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.print
+     */
+    public void testPrint() throws Exception {
+        final String name = "dummy print job";
+
+        final IPrintDocumentAdapter adapter = new PrintManager
+                .PrintDocumentAdapterDelegate(getActivity(), createMockAdapter());
+
+        // Valid parameters are tested in setUp()
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.print(null, adapter, null, mGoodComponentName.getPackageName(),
+                        mAppId, mUserId);
+            }
+        }, IllegalArgumentException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.print(name, null, null, mGoodComponentName.getPackageName(),
+                        mAppId, mUserId);
+            }
+        }, NullPointerException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.print(name, adapter, null, null, mAppId, mUserId);
+            }
+        }, IllegalArgumentException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.print(name, adapter, null, mBadComponentName.getPackageName(),
+                        mAppId, mUserId);
+            }
+        }, IllegalArgumentException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.print(name, adapter, null, mGoodComponentName.getPackageName(),
+                        BAD_APP_ID, mUserId);
+            }
+        }, SecurityException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.cancelPrintJob
+     */
+    public void testCancelPrintJob() throws Exception {
+        // Invalid print jobs IDs do not produce an exception
+        mIPrintManager.cancelPrintJob(mBadPrintJobId, mAppId, mUserId);
+        mIPrintManager.cancelPrintJob(null, mAppId, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), BAD_APP_ID, mUserId);
+            }
+        }, SecurityException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+
+        // Must be last as otherwise mGoodPrintJob will not be good anymore
+        mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
+    }
+
+    /**
+     * test IPrintManager.restartPrintJob
+     */
+    public void testRestartPrintJob() throws Exception {
+        mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
+
+        // Invalid print jobs IDs do not produce an exception
+        mIPrintManager.restartPrintJob(mBadPrintJobId, mAppId, mUserId);
+        mIPrintManager.restartPrintJob(null, mAppId, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), BAD_APP_ID, mUserId);
+            }
+        }, SecurityException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.addPrintJobStateChangeListener
+     */
+    public void testAddPrintJobStateChangeListener() throws Exception {
+        final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
+
+        mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.addPrintJobStateChangeListener(null, mAppId, mUserId);
+            }
+        }, NullPointerException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.addPrintJobStateChangeListener(listener, BAD_APP_ID, mUserId);
+            }
+        }, SecurityException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.removePrintJobStateChangeListener
+     */
+    public void testRemovePrintJobStateChangeListener() throws Exception {
+        final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
+
+        mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
+        mIPrintManager.removePrintJobStateChangeListener(listener, mUserId);
+
+        // Removing unknown listeners is a no-op
+        mIPrintManager.removePrintJobStateChangeListener(listener, mUserId);
+
+        mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.removePrintJobStateChangeListener(null, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.getInstalledPrintServices
+     */
+    public void testGetInstalledPrintServices() throws Exception {
+        List<PrintServiceInfo> printServices = mIPrintManager.getInstalledPrintServices(mUserId);
+        assertTrue(printServices.size() >= 2);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.getEnabledPrintServices
+     */
+    public void testGetEnabledPrintServices() throws Exception {
+        List<PrintServiceInfo> printServices = mIPrintManager.getEnabledPrintServices(mUserId);
+        assertTrue(printServices.size() >= 2);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.createPrinterDiscoverySession
+     */
+    public void testCreatePrinterDiscoverySession() throws Exception {
+        final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
+
+        mIPrintManager.createPrinterDiscoverySession(listener, mUserId);
+
+        try {
+            assertException(new Invokable() {
+                @Override
+                public void run() throws Exception {
+                    mIPrintManager.createPrinterDiscoverySession(null, mUserId);
+                }
+            }, NullPointerException.class);
+
+            // Cannot test bad user Id as these tests are allowed to call across users
+        } finally {
+            // Remove discovery session so that the next test create a new one. Usually a leaked
+            // session is removed on the next call from the print service. But in this case we want
+            // to force a new call to onPrinterDiscoverySessionStart in the next test.
+            mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId);
+        }
+    }
+
+    /**
+     * test IPrintManager.startPrinterDiscovery
+     */
+    public void testStartPrinterDiscovery() throws Exception {
+        final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
+        final List<PrinterId> goodPrinters = new ArrayList<>();
+        goodPrinters.add(mGoodPrinterId);
+
+        final List<PrinterId> badPrinters = new ArrayList<>();
+        badPrinters.add(mBadPrinterId);
+
+        final List<PrinterId> emptyPrinters = new ArrayList<>();
+
+        final List<PrinterId> nullPrinters = new ArrayList<>();
+        nullPrinters.add(null);
+
+        mIPrintManager.startPrinterDiscovery(listener, goodPrinters, mUserId);
+
+        // Bad or no printers do no cause exceptions
+        mIPrintManager.startPrinterDiscovery(listener, badPrinters, mUserId);
+        mIPrintManager.startPrinterDiscovery(listener, emptyPrinters, mUserId);
+        mIPrintManager.startPrinterDiscovery(listener, null, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.startPrinterDiscovery(listener, nullPrinters, mUserId);
+            }
+        }, NullPointerException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.startPrinterDiscovery(null, goodPrinters, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.stopPrinterDiscovery
+     */
+    public void testStopPrinterDiscovery() throws Exception {
+        final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
+
+        mIPrintManager.startPrinterDiscovery(listener, null, mUserId);
+        mIPrintManager.stopPrinterDiscovery(listener, mUserId);
+
+        // Stopping an already stopped session is a no-op
+        mIPrintManager.stopPrinterDiscovery(listener, mUserId);
+
+        mIPrintManager.startPrinterDiscovery(listener, null, mUserId);
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.stopPrinterDiscovery(null, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.validatePrinters
+     */
+    public void testValidatePrinters() throws Exception {
+        final List<PrinterId> goodPrinters = new ArrayList<>();
+        goodPrinters.add(mGoodPrinterId);
+
+        final List<PrinterId> badPrinters = new ArrayList<>();
+        badPrinters.add(mBadPrinterId);
+
+        final List<PrinterId> emptyPrinters = new ArrayList<>();
+
+        final List<PrinterId> nullPrinters = new ArrayList<>();
+        nullPrinters.add(null);
+
+        mIPrintManager.validatePrinters(goodPrinters, mUserId);
+
+        // Bad or empty list of printers do no cause exceptions
+        mIPrintManager.validatePrinters(badPrinters, mUserId);
+        mIPrintManager.validatePrinters(emptyPrinters, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.validatePrinters(null, mUserId);
+            }
+        }, NullPointerException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.validatePrinters(nullPrinters, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.startPrinterStateTracking
+     */
+    public void testStartPrinterStateTracking() throws Exception {
+        mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
+
+        // Bad printers do no cause exceptions
+        mIPrintManager.startPrinterStateTracking(mBadPrinterId, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.startPrinterStateTracking(null, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.getCustomPrinterIcon
+     */
+    public void testGetCustomPrinterIcon() throws Exception {
+        mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId);
+
+        // Bad printers do no cause exceptions
+        mIPrintManager.getCustomPrinterIcon(mBadPrinterId, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.getCustomPrinterIcon(null, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.stopPrinterStateTracking
+     */
+    public void testStopPrinterStateTracking() throws Exception {
+        mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
+        mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId);
+
+        // Stop to track a non-tracked printer is a no-op
+        mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId);
+
+        // Bad printers do no cause exceptions
+        mIPrintManager.startPrinterStateTracking(mBadPrinterId, mUserId);
+        mIPrintManager.stopPrinterStateTracking(mBadPrinterId, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.stopPrinterStateTracking(null, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.destroyPrinterDiscoverySession
+     */
+    public void testDestroyPrinterDiscoverySession() throws Exception {
+        final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
+
+        mIPrintManager.createPrinterDiscoverySession(listener, mUserId);
+        mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId);
+
+        // Destroying already destroyed session is a no-op
+        mIPrintManager.destroyPrinterDiscoverySession(listener, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.destroyPrinterDiscoverySession(null, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+}
diff --git a/core/tests/coretests/src/android/print/PrintTestActivity.java b/core/tests/coretests/src/android/print/PrintTestActivity.java
new file mode 100644
index 0000000..86074a6
--- /dev/null
+++ b/core/tests/coretests/src/android/print/PrintTestActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class PrintTestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+    }
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java
new file mode 100644
index 0000000..9c11c22
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.mockservice;
+
+public class MockPrintService extends StubbablePrintService {
+
+    private static final Object sLock = new Object();
+
+    private static PrintServiceCallbacks sCallbacks;
+
+    public static void setCallbacks(PrintServiceCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected PrintServiceCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setService(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java
new file mode 100644
index 0000000..4e89207
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.mockservice;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+
+public abstract class PrintServiceCallbacks {
+
+    private PrintService mService;
+
+    public PrintService getService() {
+        return mService;
+    }
+
+    public void setService(PrintService service) {
+        mService = service;
+    }
+
+    public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
+
+    public abstract void onRequestCancelPrintJob(PrintJob printJob);
+
+    public abstract void onPrintJobQueued(PrintJob printJob);
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
new file mode 100644
index 0000000..26b7cae
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.mockservice;
+
+import android.print.PrinterId;
+import android.printservice.CustomPrinterIconCallback;
+
+import java.util.List;
+
+public abstract class PrinterDiscoverySessionCallbacks {
+
+    private StubbablePrinterDiscoverySession mSession;
+
+    public void setSession(StubbablePrinterDiscoverySession session) {
+        mSession = session;
+    }
+
+    public StubbablePrinterDiscoverySession getSession() {
+        return mSession;
+    }
+
+    public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
+
+    public abstract void onStopPrinterDiscovery();
+
+    public abstract void onValidatePrinters(List<PrinterId> printerIds);
+
+    public abstract void onStartPrinterStateTracking(PrinterId printerId);
+
+    public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
+            CustomPrinterIconCallback callback);
+
+    public abstract void onStopPrinterStateTracking(PrinterId printerId);
+
+    public abstract void onDestroy();
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java
new file mode 100644
index 0000000..fb76e67
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/SettingsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.mockservice;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SettingsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java
new file mode 100644
index 0000000..b58b2735
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.mockservice;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+public abstract class StubbablePrintService extends PrintService {
+
+    @Override
+    public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            return new StubbablePrinterDiscoverySession(this,
+                    getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
+        }
+        return null;
+    }
+
+    @Override
+    public void onRequestCancelPrintJob(PrintJob printJob) {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onRequestCancelPrintJob(printJob);
+        }
+    }
+
+    @Override
+    public void onPrintJobQueued(PrintJob printJob) {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onPrintJobQueued(printJob);
+        }
+    }
+
+    protected abstract PrintServiceCallbacks getCallbacks();
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
new file mode 100644
index 0000000..04683f2
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.mockservice;
+
+import android.print.PrinterId;
+import android.printservice.CustomPrinterIconCallback;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+import java.util.List;
+
+public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
+    private final PrintService mService;
+    private final PrinterDiscoverySessionCallbacks mCallbacks;
+
+    public StubbablePrinterDiscoverySession(PrintService service,
+            PrinterDiscoverySessionCallbacks callbacks) {
+        mService = service;
+        mCallbacks = callbacks;
+        if (mCallbacks != null) {
+            mCallbacks.setSession(this);
+        }
+    }
+
+    public PrintService getService() {
+        return mService;
+    }
+
+    @Override
+    public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+        if (mCallbacks != null) {
+            mCallbacks.onStartPrinterDiscovery(priorityList);
+        }
+    }
+
+    @Override
+    public void onStopPrinterDiscovery() {
+        if (mCallbacks != null) {
+            mCallbacks.onStopPrinterDiscovery();
+        }
+    }
+
+    @Override
+    public void onValidatePrinters(List<PrinterId> printerIds) {
+        if (mCallbacks != null) {
+            mCallbacks.onValidatePrinters(printerIds);
+        }
+    }
+
+    @Override
+    public void onStartPrinterStateTracking(PrinterId printerId) {
+        if (mCallbacks != null) {
+            mCallbacks.onStartPrinterStateTracking(printerId);
+        }
+    }
+
+    @Override
+    public void onRequestCustomPrinterIcon(PrinterId printerId,
+            CustomPrinterIconCallback callback) {
+        if (mCallbacks != null) {
+            mCallbacks.onRequestCustomPrinterIcon(printerId, callback);
+        }
+    }
+
+    @Override
+    public void onStopPrinterStateTracking(PrinterId printerId) {
+        if (mCallbacks != null) {
+            mCallbacks.onStopPrinterStateTracking(printerId);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mCallbacks != null) {
+            mCallbacks.onDestroy();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index afd0bc4..00df87d 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -37,6 +37,7 @@
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
 
 import com.android.frameworks.coretests.R;
 
@@ -145,6 +146,40 @@
     }
 
     @SmallTest
+    public void testDragAndDrop() throws Exception {
+        final String text = "abc def ghi.";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+        onView(withId(R.id.textview)).perform(
+                mouseDragOnText(text.indexOf("d"), text.indexOf("f") + 1));
+
+        onView(withId(R.id.textview)).perform(
+                mouseDragOnText(text.indexOf("e"), text.length()));
+
+        onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
+        onView(withId(R.id.textview)).check(hasSelection(""));
+        assertNoSelectionHandles();
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
+    }
+
+    @SmallTest
+    public void testDragAndDrop_longClick() throws Exception {
+        final String text = "abc def ghi.";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+        onView(withId(R.id.textview)).perform(
+                mouseDragOnText(text.indexOf("d"), text.indexOf("f") + 1));
+
+        onView(withId(R.id.textview)).perform(
+                mouseLongClickAndDragOnText(text.indexOf("e"), text.length()));
+
+        onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
+        onView(withId(R.id.textview)).check(hasSelection(""));
+        assertNoSelectionHandles();
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
+    }
+
+    @SmallTest
     public void testSelectTextByLongClick() throws Exception {
         final String helloWorld = "Hello world!";
         onView(withId(R.id.textview)).perform(click());
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index edbfef9..5dae4a8 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -159,6 +159,22 @@
     }
 
     @SmallTest
+    public void testDragAndDrop() throws Exception {
+        final String text = "abc def ghi.";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("e")));
+
+        onView(withId(R.id.textview)).perform(
+                longPressAndDragOnText(text.indexOf("e"), text.length()));
+
+        onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
+        onView(withId(R.id.textview)).check(hasSelection(""));
+        assertNoSelectionHandles();
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
+    }
+
+    @SmallTest
     public void testDoubleTapToSelect() throws Exception {
         final String helloWorld = "Hello SuetYi!";
         onView(withId(R.id.textview)).perform(click());
diff --git a/docs/html/preview/support.jd b/docs/html/preview/support.jd
index cfd9467..8c392aa 100644
--- a/docs/html/preview/support.jd
+++ b/docs/html/preview/support.jd
@@ -160,20 +160,21 @@
         still perform BTLE and WiFi scans, but only when they are in the foreground. While in the background, those apps will get no results from BTLE and WiFi scans.</li>
     </ul>
   </li>
-  <li>Permission changes
+  <li>Accessing accounts
     <ul>
-      <li>Updated the user interface for permissions and enhanced some of the permissions
-        behaviors.</li>
-      <li>The {@link android.Manifest.permission#GET_ACCOUNTS} permission is now a member of the
-        {@link android.Manifest.permission_group#CONTACTS} permission group and it has a
-        {@code android:protectionLevel} of {@code dangerous}. This change means that when
-        targeting Android 6.0 (API level 23), you must check for and request this permission if
-        your app requires it.
+      <li>Updated the behavior of {@link android.accounts.AccountManager} account
+        discovery methods.
       </li>
-
-      <li>The {@code android.permission.READ_PROFILE} and {@code android.permission.WRITE_PROFILE}
-       permissions have been removed from the {@link android.Manifest.permission_group#CONTACTS}
-       permission group.
+      <li>The GET_ACCOUNTS permission has been deprecated.
+      </li>
+      <li>Apps targeting API level 24 should start the intent returned by
+        newChooseAccountIntent(...) and await the result to acquire a reference
+        to the user's selected account. AccountManager methods like getAccounts and
+        related methods will only return those accounts managed by
+        authenticators that match the signatures of the calling app.
+      </li>
+      <li>Apps targeting API level 23 or earlier will continue to behave as
+        before.
       </li>
     </ul>
   </li>
diff --git a/docs/html/training/id-auth/identify.jd b/docs/html/training/id-auth/identify.jd
index db9ab3a..4c399f9 100644
--- a/docs/html/training/id-auth/identify.jd
+++ b/docs/html/training/id-auth/identify.jd
@@ -15,8 +15,7 @@
 <ol>
   <li><a href="#ForYou">Determine if AccountManager is for You</a></li>
   <li><a href="#TaskTwo">Decide What Type of Account to Use</a></li>
-  <li><a href="#GetPermission">Request GET_ACCOUNT permission</a></li>
-  <li><a href="#TaskFive">Query AccountManager for a List of Accounts</a></li>
+  <li><a href="#QueryAccounts">Query the user for an Account</a></li>
   <li><a href="#IdentifyUser">Use the Account Object to Personalize Your App</a></li>
   <li><a href="#IdIsEnough">Decide Whether an Account Name is Enough</a></li>
 </ol>
@@ -71,48 +70,46 @@
 <h2 id="TaskTwo">Decide What Type of Account to Use</h2>
 
 <p>Android devices can store multiple accounts from many different providers.
-When you query {@link android.accounts.AccountManager} for account names, you can choose to filter
-by
-account type. The account type is a string that uniquely identifies the entity
-that issued the account. For instance, Google accounts have type "com.google,"
-while Twitter uses "com.twitter.android.auth.login."</p>
+When you query {@link android.accounts.AccountManager} for account names, you
+can choose to filter by account type. The account type is a string that
+uniquely identifies the entity that issued the account. For instance, Google
+accounts have type "com.google," while Twitter uses
+"com.twitter.android.auth.login."</p>
 
+<h2 id="QueryAccounts">Query the user for an Account</h2>
 
-<h2 id="GetPermission">Request GET_ACCOUNT permission</h2>
-
-<p>In order to get a list of accounts on the device, your app needs the {@link
-android.Manifest.permission#GET_ACCOUNTS}
-permission. Add a <a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code
-<uses-permission>}</a> tag in your manifest file to request
-this permission:</p>
+<p>Once an account type has been determined, you can prompt the user with an
+account chooser as follows:
 
 <pre>
-&lt;manifest ... >
-    &lt;uses-permission android:name="android.permission.GET_ACCOUNTS" /&gt;
-    ...
-&lt;/manifest>
+AccountManager am = AccountManager.get(this);  // "this" reference the current Context
+Intent chooserIntent = am.newChooseAccountIntent(
+        null, // currently select account
+        null, // list of accounts that are allowed to be shown
+        new String[] { "com.google" }, // Only allow the user to select Google accounts
+        false,
+        null,  // description text
+        null, // add account auth token type
+        null, // required features for added accounts
+        null);  // options for adding an account
+this.startActivityForResult(chooserIntent, MY_REQUEST_CODE);
 </pre>
 
-
-<h2 id="TaskFive">Query AccountManager for a List of Accounts</h2>
-
-<p>Once you decide what account type you're interested in, you need to query for accounts of that
-type. Get an instance of {@link android.accounts.AccountManager} by calling {@link
-android.accounts.AccountManager#get(android.content.Context) AccountManager.get()}. Then use that
-instance to call {@link android.accounts.AccountManager#getAccountsByType(java.lang.String)
-getAccountsByType()}.</p>
+<p>Once the chooser intent is started, the user will be presented with a list of
+appropriately typed accounts. From this list they will select one which will be
+returned to your app upon onActivityResult as follows:
 
 <pre>
-AccountManager am = AccountManager.get(this); // "this" references the current Context
-
-Account[] accounts = am.getAccountsByType("com.google");
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    if (requestCode == MY_REQUEST_CODE && resultCode == RESULT_OK) {
+        String name = data.getStringExtra(AccountManage.KEY_ACCOUNT_NAME);
+        String type = data.getStringExtra(AccountManage.KEY_ACCOUNT_TYPE);
+        Account selectedAccount = new Account(name, type);
+        doSomethingWithSelectedAccount(selectedAccount);
+    }
+}
 </pre>
 
-<p>This returns an array of {@link android.accounts.Account} objects. If there's more than one
-{@link android.accounts.Account} in
-the array, you should present a dialog asking the user to select one.</p>
-
-
 <h2 id="IdentifyUser">Use the Account Object to Personalize Your App</h2>
 
 <p>The {@link android.accounts.Account} object contains an account name, which for Google accounts
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 84ca546..1857345 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -19,7 +19,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.Animator.AnimatorListener;
-import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.ColorStateList;
@@ -141,15 +140,6 @@
     /** Local, mutable animator set. */
     private final AnimatorSet mAnimatorSet = new AnimatorSet();
 
-
-    private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    invalidateSelf();
-                }
-            };
-
     /**
      * The resources against which this drawable was created. Used to attempt
      * to inflate animators if applyTheme() doesn't get called.
@@ -211,6 +201,9 @@
     @Override
     public void draw(Canvas canvas) {
         mAnimatedVectorState.mVectorDrawable.draw(canvas);
+        if (isStarted()) {
+            invalidateSelf();
+        }
     }
 
     @Override
@@ -493,7 +486,6 @@
          *            animators, or {@code null} if not available
          */
         public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet,
-                @NonNull ValueAnimator.AnimatorUpdateListener updateListener,
                 @Nullable Resources res) {
             // Check for uninflated animators. We can remove this after we add
             // support for Animator.applyTheme(). See comments in inflate().
@@ -519,17 +511,6 @@
                     final Animator nextAnim = prepareLocalAnimator(i);
                     builder.with(nextAnim);
                 }
-
-                // Setup a value animator to get animation update callbacks.
-                long totalDuration = animatorSet.getTotalDuration();
-                ValueAnimator updateAnim = ValueAnimator.ofFloat(0f, 1f);
-                if (totalDuration == ValueAnimator.DURATION_INFINITE) {
-                    updateAnim.setRepeatCount(ValueAnimator.INFINITE);
-                } else {
-                    updateAnim.setDuration(totalDuration);
-                }
-                updateAnim.addUpdateListener(updateListener);
-                builder.with(updateAnim);
             }
         }
 
@@ -622,7 +603,7 @@
     @NonNull
     private void ensureAnimatorSet() {
         if (!mHasAnimatorSet) {
-            mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mUpdateListener, mRes);
+            mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes);
             mHasAnimatorSet = true;
             mRes = null;
         }
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 483ccf7..fc40554 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -4,6 +4,11 @@
 
 HWUI_NEW_OPS := true
 
+# Enables fine-grained GLES error checking
+# If set to true, every GLES call is wrapped & error checked
+# Has moderate overhead
+HWUI_ENABLE_OPENGL_VALIDATION := false
+
 hwui_src_files := \
     font/CacheTexture.cpp \
     font/Font.cpp \
@@ -157,6 +162,13 @@
         frameworks/rs
 endif
 
+ifeq (true, $(HWUI_ENABLE_OPENGL_VALIDATION))
+    hwui_cflags += -include debug/wrap_gles.h
+    hwui_src_files += debug/wrap_gles.cpp
+    hwui_c_includes += frameworks/native/opengl/libs/GLES2
+    hwui_cflags += -DDEBUG_OPENGL=3
+endif
+
 
 # ------------------------
 # static library
@@ -188,8 +200,8 @@
         -DHWUI_NULL_GPU
 LOCAL_SRC_FILES := \
         $(hwui_src_files) \
-        tests/common/nullegl.cpp \
-        tests/common/nullgles.cpp
+        debug/nullegl.cpp \
+        debug/nullgles.cpp
 LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
 
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 00381ee..7ecc743 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -784,9 +784,7 @@
                 .build();
         renderer.renderGlop(state, glop);
     }
-    GL_CHECKPOINT();
     renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
-    GL_CHECKPOINT();
 }
 
 } // namespace uirenderer
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index a808b88..5736c70 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -49,7 +49,8 @@
     // attach the texture to the FBO
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
             offscreenBuffer->texture.id(), 0);
-    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
+    GL_CHECKPOINT(LOW);
+
     LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
             "framebuffer incomplete!");
 
@@ -63,7 +64,7 @@
     if (mRenderTarget.stencil) {
         // if stencil was used for clipping, detach it and return it to pool
         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
-        LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "glfbrb endlayer failed");
+        GL_CHECKPOINT(MODERATE);
         mCaches.renderBufferCache.put(mRenderTarget.stencil);
         mRenderTarget.stencil = nullptr;
     }
@@ -74,8 +75,7 @@
 
     // Detach the texture from the FBO
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
-    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED, bound fbo = %u",
-            mRenderState.getFramebuffer());
+    GL_CHECKPOINT(LOW);
     mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
     mRenderTarget.frameBufferId = 0;
 }
@@ -139,8 +139,6 @@
     mCaches.pathCache.trim();
     mCaches.tessellationCache.trim();
 
-    GL_CHECKPOINT();
-
 #if DEBUG_MEMORY_USAGE
     mCaches.dumpMemoryUsage();
 #else
diff --git a/libs/hwui/BufferPool.h b/libs/hwui/BufferPool.h
index 9bda233..005b399 100644
--- a/libs/hwui/BufferPool.h
+++ b/libs/hwui/BufferPool.h
@@ -16,8 +16,9 @@
 
 #pragma once
 
-#include <utils/RefBase.h>
-#include <utils/Log.h>
+#include "utils/RefBase.h"
+#include "utils/Log.h"
+#include "utils/Macros.h"
 
 #include <atomic>
 #include <stdint.h>
@@ -37,6 +38,7 @@
 class BufferPool : public VirtualLightRefBase {
 public:
     class Buffer {
+        PREVENT_COPY_AND_ASSIGN(Buffer);
     public:
         int64_t* getBuffer() { return mBuffer.get(); }
         size_t getSize() { return mSize; }
@@ -57,14 +59,17 @@
             return refs - 1;
         }
 
+        bool isUniqueRef() {
+            return mRefs.load() == 1;
+        }
+
     private:
         friend class BufferPool;
 
-        Buffer(BufferPool* pool, size_t size) {
+        Buffer(BufferPool* pool, size_t size) : mRefs(1) {
             mSize = size;
             mBuffer.reset(new int64_t[size]);
             mPool = pool;
-            mRefs++;
         }
 
         void setPool(BufferPool* pool) {
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index 0643a54..9dfe454 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -27,6 +27,22 @@
 
 namespace android {
 
+namespace SaveFlags {
+
+// These must match the corresponding Canvas API constants.
+enum {
+    Matrix        = 0x01,
+    Clip          = 0x02,
+    HasAlphaLayer = 0x04,
+    ClipToLayer   = 0x10,
+
+    // Helper constant
+    MatrixClip    = Matrix | Clip,
+};
+typedef uint32_t Flags;
+
+} // namespace SaveFlags
+
 class ANDROID_API Canvas {
 public:
     virtual ~Canvas() {};
@@ -70,16 +86,17 @@
 // ----------------------------------------------------------------------------
 // Canvas state operations
 // ----------------------------------------------------------------------------
+
     // Save (layer)
     virtual int getSaveCount() const = 0;
-    virtual int save(SkCanvas::SaveFlags flags) = 0;
+    virtual int save(SaveFlags::Flags flags) = 0;
     virtual void restore() = 0;
     virtual void restoreToCount(int saveCount) = 0;
 
     virtual int saveLayer(float left, float top, float right, float bottom,
-                const SkPaint* paint, SkCanvas::SaveFlags flags) = 0;
+                const SkPaint* paint, SaveFlags::Flags flags) = 0;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom,
-            int alpha, SkCanvas::SaveFlags flags) = 0;
+            int alpha, SaveFlags::Flags flags) = 0;
 
     // Matrix
     virtual void getMatrix(SkMatrix* outMatrix) const = 0;
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index cf2726b..43ff33f 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#include <SkCanvas.h>
-
+#include "Canvas.h"
 #include "CanvasState.h"
 #include "utils/MathUtils.h"
 
@@ -54,8 +53,7 @@
     }
 
     freeAllSnapshots();
-    mSnapshot = allocSnapshot(&mFirstSnapshot,
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
     mSnapshot->setRelativeLightCenter(Vector3());
     mSaveCount = 1;
 }
@@ -72,8 +70,7 @@
     }
 
     freeAllSnapshots();
-    mSnapshot = allocSnapshot(&mFirstSnapshot,
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
     mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
     mSnapshot->fbo = mCanvas.getTargetFbo();
     mSnapshot->setRelativeLightCenter(lightCenter);
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index e98fa04..748edef 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -17,8 +17,18 @@
 #ifndef ANDROID_HWUI_DEBUG_H
 #define ANDROID_HWUI_DEBUG_H
 
+#define DEBUG_LEVEL_HIGH 3
+#define DEBUG_LEVEL_MODERATE 2
+#define DEBUG_LEVEL_LOW 1
+#define DEBUG_LEVEL_NONE 0
+
 // Turn on to check for OpenGL errors on each frame
-#define DEBUG_OPENGL 1
+// Note DEBUG_LEVEL_HIGH for DEBUG_OPENGL is only setable by enabling
+// HWUI_ENABLE_OPENGL_VALIDATION when building HWUI. Similarly if
+// HWUI_ENABLE_OPENGL_VALIDATION is set then this is always DEBUG_LEVEL_HIGH
+#ifndef DEBUG_OPENGL
+#define DEBUG_OPENGL DEBUG_LEVEL_LOW
+#endif
 
 // Turn on to enable initialization information
 #define DEBUG_INIT 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index a1825c5..1b0f424 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include <SkCanvas.h>
-
 #include <utils/Trace.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -419,7 +417,7 @@
  * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame.
  *
  * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a
- * complex clip, and if the flags (kClip_SaveFlag & kClipToLayer_SaveFlag) are set.
+ * complex clip, and if the flags (SaveFlags::Clip & SaveFlags::ClipToLayer) are set.
  */
 void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer,
         SaveLayerOp* op, int newSaveCount) {
@@ -438,7 +436,7 @@
     int saveFlags = op->getFlags();
     DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount);
 
-    if (recordingComplexClip() && (saveFlags & SkCanvas::kClip_SaveFlag)) {
+    if (recordingComplexClip() && (saveFlags & SaveFlags::Clip)) {
         // store and replay the save operation, as it may be needed to correctly playback the clip
         DEFER_LOGD("    adding save barrier with new save count %d", newSaveCount);
         storeStateOpBarrier(renderer, op);
@@ -621,7 +619,7 @@
             this, newSaveCount, mBatches.size());
 
     // store displayState for the restore operation, as it may be associated with a saveLayer that
-    // doesn't have kClip_SaveFlag set
+    // doesn't have SaveFlags::Clip set
     DeferredDisplayState* state = createState();
     renderer.storeDisplayState(*state, getStateOpDeferFlags());
     mBatches.push_back(new RestoreToCountBatch(op, state, newSaveCount));
@@ -654,7 +652,7 @@
     renderer.eventMark("Flush");
 
     // save and restore so that reordering doesn't affect final state
-    renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    renderer.save(SaveFlags::MatrixClip);
 
     if (CC_LIKELY(avoidOverdraw())) {
         for (unsigned int i = 1; i < mBatches.size(); i++) {
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 759c12a..384e64d 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -102,7 +102,7 @@
     return mSkiaCanvasProxy.get();
 }
 
-int DisplayListCanvas::save(SkCanvas::SaveFlags flags) {
+int DisplayListCanvas::save(SaveFlags::Flags flags) {
     addStateOp(new (alloc()) SaveOp((int) flags));
     return mState.save((int) flags);
 }
@@ -125,9 +125,9 @@
 }
 
 int DisplayListCanvas::saveLayer(float left, float top, float right, float bottom,
-        const SkPaint* paint, SkCanvas::SaveFlags flags) {
+        const SkPaint* paint, SaveFlags::Flags flags) {
     // force matrix/clip isolation for layer
-    flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
+    flags |= SaveFlags::MatrixClip;
 
     paint = refPaint(paint);
     addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, (int) flags));
@@ -232,7 +232,7 @@
 
 void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top,
         const SkPaint* paint) {
-    save(SkCanvas::kMatrix_SaveFlag);
+    save(SaveFlags::Matrix);
     translate(left, top);
     drawBitmap(&bitmap, paint);
     restore();
@@ -253,7 +253,7 @@
         drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
                    dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
     } else {
-        save(SkCanvas::kMatrix_SaveFlag);
+        save(SaveFlags::Matrix);
         concat(matrix);
         drawBitmap(&bitmap, paint);
         restore();
@@ -269,7 +269,7 @@
             && (srcBottom - srcTop == dstBottom - dstTop)
             && (srcRight - srcLeft == dstRight - dstLeft)) {
         // transform simple rect to rect drawing case into position bitmap ops, since they merge
-        save(SkCanvas::kMatrix_SaveFlag);
+        save(SaveFlags::Matrix);
         translate(dstLeft, dstTop);
         drawBitmap(&bitmap, paint);
         restore();
@@ -283,7 +283,7 @@
                 // Apply the scale transform on the canvas, so that the shader
                 // effectively calculates positions relative to src rect space
 
-                save(SkCanvas::kMatrix_SaveFlag);
+                save(SaveFlags::Matrix);
                 translate(dstLeft, dstTop);
                 scale(scaleX, scaleY);
 
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 72fc100..f1cfa08 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -128,14 +128,14 @@
 // ----------------------------------------------------------------------------
     // Save (layer)
     virtual int getSaveCount() const override { return mState.getSaveCount(); }
-    virtual int save(SkCanvas::SaveFlags flags) override;
+    virtual int save(SaveFlags::Flags flags) override;
     virtual void restore() override;
     virtual void restoreToCount(int saveCount) override;
 
     virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
-        SkCanvas::SaveFlags flags) override;
+        SaveFlags::Flags flags) override;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom,
-            int alpha, SkCanvas::SaveFlags flags) override {
+            int alpha, SaveFlags::Flags flags) override {
         SkPaint paint;
         paint.setAlpha(alpha);
         return saveLayer(left, top, right, bottom, &paint, flags);
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index c4c655b..a457212 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -16,6 +16,7 @@
 
 #include "FrameBuilder.h"
 
+#include "Canvas.h"
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
 #include "renderstate/OffscreenBufferPool.h"
@@ -23,7 +24,6 @@
 #include "utils/PaintUtils.h"
 #include "utils/TraceUtils.h"
 
-#include <SkCanvas.h>
 #include <SkPathOps.h>
 #include <utils/TypeHelpers.h>
 
@@ -33,6 +33,15 @@
 FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
         uint32_t viewportWidth, uint32_t viewportHeight,
         const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
+        : FrameBuilder(layers, clip, viewportWidth, viewportHeight, nodes, lightCenter,
+                Rect(0, 0, 0, 0)) {
+}
+
+
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+        uint32_t viewportWidth, uint32_t viewportHeight,
+        const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter,
+        const Rect &contentDrawBounds)
         : mCanvasState(*this) {
     ATRACE_NAME("prepare drawing commands");
 
@@ -72,14 +81,56 @@
         }
     }
 
-    // Defer Fbo0
+    // It there are multiple render nodes, they are laid out as follows:
+    // #0 - backdrop (content + caption)
+    // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
+    // #2 - additional overlay nodes
+    // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
+    // resizing however it might become partially visible. The following render loop will crop the
+    // backdrop against the content and draw the remaining part of it. It will then draw the content
+    // cropped to the backdrop (since that indicates a shrinking of the window).
+    //
+    // Additional nodes will be drawn on top with no particular clipping semantics.
+
+    // The bounds of the backdrop against which the content should be clipped.
+    Rect backdropBounds = contentDrawBounds;
+    // Usually the contents bounds should be mContentDrawBounds - however - we will
+    // move it towards the fixed edge to give it a more stable appearance (for the moment).
+    // If there is no content bounds we ignore the layering as stated above and start with 2.
+    int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;
+
     for (const sp<RenderNode>& node : nodes) {
         if (node->nothingToDraw()) continue;
         node->computeOrdering();
+        int count = mCanvasState.save(SaveFlags::MatrixClip);
 
-        int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+        if (layer == 0) {
+            const RenderProperties& properties = node->properties();
+            Rect targetBounds(properties.getLeft(), properties.getTop(),
+                              properties.getRight(), properties.getBottom());
+            // Move the content bounds towards the fixed corner of the backdrop.
+            const int x = targetBounds.left;
+            const int y = targetBounds.top;
+            // Remember the intersection of the target bounds and the intersection bounds against
+            // which we have to crop the content.
+            backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
+            backdropBounds.doIntersect(targetBounds);
+        } else if (layer == 1) {
+            // We shift and clip the content to match its final location in the window.
+            const float left = contentDrawBounds.left;
+            const float top = contentDrawBounds.top;
+            const float dx = backdropBounds.left - left;
+            const float dy = backdropBounds.top - top;
+            const float width = backdropBounds.getWidth();
+            const float height = backdropBounds.getHeight();
+            mCanvasState.translate(dx, dy);
+            // It gets cropped against the bounds of the backdrop to stay inside.
+            mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op);
+        }
+
         deferNodePropsAndOps(*node);
         mCanvasState.restoreToCount(count);
+        layer++;
     }
 }
 
@@ -327,7 +378,7 @@
 
 void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
     const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
-    int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    int count = mCanvasState.save(SaveFlags::MatrixClip);
 
     // can't be null, since DL=null node rejection happens before deferNodePropsAndOps
     const DisplayList& displayList = *(renderNode.getDisplayList());
@@ -348,7 +399,7 @@
     for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) {
         RenderNodeOp* childOp = renderNode.mProjectedNodes[i];
 
-        int restoreTo = mCanvasState.save(SkCanvas::kMatrix_SaveFlag);
+        int restoreTo = mCanvasState.save(SaveFlags::Matrix);
         mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);
         deferRenderNodeOpImpl(*childOp);
         mCanvasState.restoreToCount(restoreTo);
@@ -392,7 +443,7 @@
 
 void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) {
     if (op.renderNode->nothingToDraw()) return;
-    int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+    int count = mCanvasState.save(SaveFlags::MatrixClip);
 
     // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix)
     mCanvasState.writableSnapshot()->mutateClipArea().applyClip(op.localClip,
@@ -450,6 +501,10 @@
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
+    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
+
+    // TODO: Fix this ( b/26569206 )
+/*
     // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation
     // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
     // MergingDrawBatch::canMergeWith()
@@ -464,6 +519,7 @@
     } else {
         currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
     }
+*/
 }
 
 void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
@@ -597,7 +653,7 @@
         const Rect& repaintRect,
         const Vector3& lightCenter,
         const BeginLayerOp* beginLayerOp, RenderNode* renderNode) {
-    mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+    mCanvasState.save(SaveFlags::MatrixClip);
     mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
     mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
     mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter);
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index bd01850..dea9934 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -59,6 +59,11 @@
             uint32_t viewportWidth, uint32_t viewportHeight,
             const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
 
+    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+            uint32_t viewportWidth, uint32_t viewportHeight,
+            const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter,
+            const Rect &contentDrawBounds);
+
     virtual ~FrameBuilder() {}
 
     /**
@@ -100,30 +105,30 @@
         // Relay through layers in reverse order, since layers
         // later in the list will be drawn by earlier ones
         for (int i = mLayerBuilders.size() - 1; i >= 1; i--) {
-            GL_CHECKPOINT();
+            GL_CHECKPOINT(MODERATE);
             LayerBuilder& layer = *(mLayerBuilders[i]);
             if (layer.renderNode) {
                 // cached HW layer - can't skip layer if empty
                 renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
-                GL_CHECKPOINT();
+                GL_CHECKPOINT(MODERATE);
                 layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
-                GL_CHECKPOINT();
+                GL_CHECKPOINT(MODERATE);
                 renderer.endLayer();
             } else if (!layer.empty()) { // save layer - skip entire layer if empty
                 layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
-                GL_CHECKPOINT();
+                GL_CHECKPOINT(MODERATE);
                 layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
-                GL_CHECKPOINT();
+                GL_CHECKPOINT(MODERATE);
                 renderer.endLayer();
             }
         }
 
-        GL_CHECKPOINT();
+        GL_CHECKPOINT(MODERATE);
         const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
         renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
-        GL_CHECKPOINT();
+        GL_CHECKPOINT(MODERATE);
         fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
-        GL_CHECKPOINT();
+        GL_CHECKPOINT(MODERATE);
         renderer.endFrame(fbo0.repaintRect);
     }
 
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 0f219e4..3123e8e 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -407,7 +407,6 @@
         renderState.bindFramebuffer(fbo);
 
         glGenTextures(1, &texture);
-        GL_CHECKPOINT();
 
         caches.textureState().activateTexture(0);
         caches.textureState().bindTexture(texture);
@@ -422,11 +421,9 @@
 
         glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(),
                 0, format, type, nullptr);
-        GL_CHECKPOINT();
 
         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                 GL_TEXTURE_2D, texture, 0);
-        GL_CHECKPOINT();
 
         {
             LayerRenderer renderer(renderState, layer);
@@ -437,8 +434,6 @@
             renderer.translate(0.0f, bitmap->height());
             renderer.scale(1.0f, -1.0f);
 
-            GL_CHECKPOINT();
-
             {
                 Rect bounds;
                 bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
@@ -447,7 +442,6 @@
                 glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
                         type, bitmap->getPixels());
 
-                GL_CHECKPOINT();
             }
 
             status = true;
@@ -460,6 +454,8 @@
         renderState.deleteFramebuffer(fbo);
         renderState.setViewport(previousViewportWidth, previousViewportHeight);
 
+        GL_CHECKPOINT(MODERATE);
+
         return status;
     }
     return false;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index db017fe..6c2e244 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -17,6 +17,7 @@
 #include <GpuMemoryTracker.h>
 #include "OpenGLRenderer.h"
 
+#include "Canvas.h"
 #include "DeferredDisplayList.h"
 #include "GammaFontRenderer.h"
 #include "Glop.h"
@@ -39,7 +40,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <SkCanvas.h>
 #include <SkColor.h>
 #include <SkPaintDefaults.h>
 #include <SkPathOps.h>
@@ -195,7 +195,7 @@
     }
 
     if (!suppressErrorChecks()) {
-        GL_CHECKPOINT();
+        GL_CHECKPOINT(MODERATE);
 
 #if DEBUG_MEMORY_USAGE
         mCaches.dumpMemoryUsage();
@@ -472,7 +472,7 @@
 int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
         const SkPaint* paint, int flags, const SkPath* convexMask) {
     // force matrix/clip isolation for layer
-    flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
+    flags |= SaveFlags::MatrixClip;
 
     const int count = mState.saveSnapshot(flags);
 
@@ -531,7 +531,7 @@
         const SkPaint* paint, int flags) {
     const int count = mState.saveSnapshot(flags);
 
-    if (!mState.currentlyIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
+    if (!mState.currentlyIgnored() && (flags & SaveFlags::ClipToLayer)) {
         // initialize the snapshot as though it almost represents an FBO layer so deferred draw
         // operations will be able to store and restore the current clip and transform info, and
         // quick rejection will be correct (for display lists)
@@ -558,7 +558,7 @@
  * and the frame buffer still receive every drawing command. For instance, if a
  * layer is created and a shape intersecting the bounds of the layers and the
  * framebuffer is draw, the shape will be drawn on both (unless the layer was
- * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
+ * created with the SaveFlags::ClipToLayer flag.)
  *
  * A way to implement layers is to create an FBO for each layer, backed by an RGBA
  * texture. Unfortunately, this is inefficient as it requires every primitive to
@@ -608,7 +608,7 @@
     LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
     LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
 
-    const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
+    const bool fboLayer = flags & SaveFlags::ClipToLayer;
 
     // Window coordinates of the layer
     Rect clip;
@@ -890,7 +890,7 @@
     if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
 
     if (layer->getConvexMask()) {
-        save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+        save(SaveFlags::MatrixClip);
 
         // clip to the area of the layer the mask can be larger
         clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op);
@@ -2233,7 +2233,7 @@
     if (layer->isTextureLayer()) {
         transform = &layer->getTransform();
         if (!transform->isIdentity()) {
-            save(SkCanvas::kMatrix_SaveFlag);
+            save(SaveFlags::Matrix);
             concatMatrix(*transform);
         }
     }
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 78855e5..328e291 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -92,7 +92,7 @@
 // android/graphics/Canvas state operations
 // ----------------------------------------------------------------------------
 // Save (layer)
-int RecordingCanvas::save(SkCanvas::SaveFlags flags) {
+int RecordingCanvas::save(SaveFlags::Flags flags) {
     return mState.save((int) flags);
 }
 
@@ -105,10 +105,10 @@
 }
 
 int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
-        const SkPaint* paint, SkCanvas::SaveFlags flags) {
+        const SkPaint* paint, SaveFlags::Flags flags) {
     // force matrix/clip isolation for layer
-    flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
-    bool clippedLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
+    flags |= SaveFlags::MatrixClip;
+    bool clippedLayer = flags & SaveFlags::ClipToLayer;
 
     const Snapshot& previous = *mState.currentSnapshot();
 
@@ -128,7 +128,7 @@
         // unlikely case where an unclipped savelayer is recorded with a clip it can use,
         // as none of its unaffected/unclipped area is visible
         clippedLayer = true;
-        flags |= SkCanvas::kClipToLayer_SaveFlag;
+        flags |= SaveFlags::ClipToLayer;
     }
 
     visibleBounds.doIntersect(previous.getRenderTargetClip());
@@ -424,7 +424,7 @@
 
 // Bitmap-based
 void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
-    save(SkCanvas::kMatrix_SaveFlag);
+    save(SaveFlags::Matrix);
     translate(left, top);
     drawBitmap(&bitmap, paint);
     restore();
@@ -445,7 +445,7 @@
         drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
                    dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
     } else {
-        save(SkCanvas::kMatrix_SaveFlag);
+        save(SaveFlags::Matrix);
         concat(matrix);
         drawBitmap(&bitmap, paint);
         restore();
@@ -461,7 +461,7 @@
             && (srcBottom - srcTop == dstBottom - dstTop)
             && (srcRight - srcLeft == dstRight - dstLeft)) {
         // transform simple rect to rect drawing case into position bitmap ops, since they merge
-        save(SkCanvas::kMatrix_SaveFlag);
+        save(SaveFlags::Matrix);
         translate(dstLeft, dstTop);
         drawBitmap(&bitmap, paint);
         restore();
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 8aa7506..786f96e 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -108,14 +108,14 @@
 // ----------------------------------------------------------------------------
     // Save (layer)
     virtual int getSaveCount() const override { return mState.getSaveCount(); }
-    virtual int save(SkCanvas::SaveFlags flags) override;
+    virtual int save(SaveFlags::Flags flags) override;
     virtual void restore() override;
     virtual void restoreToCount(int saveCount) override;
 
     virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
-        SkCanvas::SaveFlags flags) override;
+        SaveFlags::Flags flags) override;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom,
-            int alpha, SkCanvas::SaveFlags flags) override {
+            int alpha, SaveFlags::Flags flags) override {
         SkPaint paint;
         paint.setAlpha(alpha);
         return saveLayer(left, top, right, bottom, &paint, flags);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index ae690fd..d4588ed 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -33,8 +33,6 @@
 #include "protos/hwui.pb.h"
 #include "protos/ProtoHelpers.h"
 
-#include <SkCanvas.h>
-
 #include <algorithm>
 #include <sstream>
 #include <string>
@@ -105,8 +103,7 @@
             (isRenderable() ? "" : ", empty"),
             (properties().getProjectBackwards() ? ", projected" : ""),
             (mLayer != nullptr ? ", on HW Layer" : ""));
-    ALOGD("%*s%s %d", level * 2, "", "Save",
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    ALOGD("%*s%s %d", level * 2, "", "Save", SaveFlags::MatrixClip);
 
     properties().debugOutputProperties(level);
 
@@ -574,7 +571,7 @@
                     layerBounds.left, layerBounds.top,
                     layerBounds.right, layerBounds.bottom,
                     (int) (properties().getAlpha() * 255),
-                    SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag);
+                    SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer);
             handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
         }
 
@@ -875,7 +872,7 @@
 
     // Apply the base transform of the parent of the 3d children. This isolates
     // 3d children of the current chunk from transformations made in previous chunks.
-    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+    int rootRestoreTo = renderer.save(SaveFlags::Matrix);
     renderer.setGlobalMatrix(initialTransform);
 
     /**
@@ -919,7 +916,7 @@
 
         // only the actual child DL draw needs to be in save/restore,
         // since it modifies the renderer's matrix
-        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+        int restoreTo = renderer.save(SaveFlags::Matrix);
 
         DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
 
@@ -941,7 +938,7 @@
     int restoreTo = renderer.getSaveCount();
 
     LinearAllocator& alloc = handler.allocator();
-    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+    handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
             PROPERTY_SAVECOUNT, properties().getClipToBounds());
 
     // Transform renderer to match background we're projecting onto
@@ -966,7 +963,7 @@
         renderNodeOp_t* childOp = mProjectedNodes[i];
 
         // matrix save, concat, and restore can be done safely without allocating operations
-        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+        int restoreTo = renderer.save(SaveFlags::Matrix);
         renderer.concatMatrix(childOp->transformFromCompositingAncestor);
         childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
         handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
@@ -1027,11 +1024,11 @@
 
     LinearAllocator& alloc = handler.allocator();
     int restoreTo = renderer.getSaveCount();
-    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+    handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
             PROPERTY_SAVECOUNT, properties().getClipToBounds());
 
     DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "",
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+            SaveFlags::MatrixClip, restoreTo);
 
     if (useViewProperties) {
         setViewProperties<T>(renderer, handler);
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ce1bd6a..b848af4 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -18,12 +18,12 @@
 
 #include <utils/Trace.h>
 
-#include <SkCanvas.h>
 #include <SkColorFilter.h>
 #include <SkMatrix.h>
 #include <SkPath.h>
 #include <SkPathOps.h>
 
+#include "Canvas.h"
 #include "Matrix.h"
 #include "OpenGLRenderer.h"
 #include "utils/MathUtils.h"
@@ -144,7 +144,7 @@
                     (int)layerBounds.left, (int)layerBounds.top,
                     (int)layerBounds.right, (int)layerBounds.bottom,
                     (int)(mPrimitiveFields.mAlpha * 255),
-                    SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kClipToLayer_SaveFlag);
+                    SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer);
         }
 
 
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 96c1a7c..20e7c71 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -24,6 +24,7 @@
 #include <SkGraphics.h>
 #include <SkShader.h>
 #include <SkTArray.h>
+#include <SkTLazy.h>
 #include <SkTemplates.h>
 
 #include <memory>
@@ -63,14 +64,14 @@
     virtual bool isHighContrastText() override { return mHighContrastText; }
 
     virtual int getSaveCount() const override;
-    virtual int save(SkCanvas::SaveFlags flags) override;
+    virtual int save(SaveFlags::Flags flags) override;
     virtual void restore() override;
     virtual void restoreToCount(int saveCount) override;
 
     virtual int saveLayer(float left, float top, float right, float bottom,
-                const SkPaint* paint, SkCanvas::SaveFlags flags) override;
+                const SkPaint* paint, SaveFlags::Flags flags) override;
     virtual int saveLayerAlpha(float left, float top, float right, float bottom,
-            int alpha, SkCanvas::SaveFlags flags) override;
+            int alpha, SaveFlags::Flags flags) override;
 
     virtual void getMatrix(SkMatrix* outMatrix) const override;
     virtual void setMatrix(const SkMatrix& matrix) override;
@@ -138,13 +139,13 @@
 
 private:
     struct SaveRec {
-        int                 saveCount;
-        SkCanvas::SaveFlags saveFlags;
+        int              saveCount;
+        SaveFlags::Flags saveFlags;
     };
 
     bool mHighContrastText = false;
 
-    void recordPartialSave(SkCanvas::SaveFlags flags);
+    void recordPartialSave(SaveFlags::Flags flags);
     void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
     void applyClips(const SkTArray<SkClipStack::Element>& clips);
 
@@ -231,7 +232,7 @@
     return mCanvas->getSaveCount();
 }
 
-int SkiaCanvas::save(SkCanvas::SaveFlags flags) {
+int SkiaCanvas::save(SaveFlags::Flags flags) {
     int count = mCanvas->save();
     recordPartialSave(flags);
     return count;
@@ -254,8 +255,8 @@
         return;
     }
 
-    bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
-    bool preserveClip   = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+    bool preserveMatrix = !(rec->saveFlags & SaveFlags::Matrix);
+    bool preserveClip   = !(rec->saveFlags & SaveFlags::Clip);
 
     SkMatrix savedMatrix;
     if (preserveMatrix) {
@@ -291,34 +292,53 @@
     }
 }
 
+static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) {
+    SkCanvas::SaveLayerFlags layerFlags = 0;
+
+    if (!(flags & SaveFlags::HasAlphaLayer)) {
+        layerFlags |= SkCanvas::kIsOpaque_SaveLayerFlag;
+    }
+
+    if (!(flags & SaveFlags::ClipToLayer)) {
+        layerFlags |= SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag;
+    }
+
+    return layerFlags;
+}
+
 int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
-            const SkPaint* paint, SkCanvas::SaveFlags flags) {
-    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
-    int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag);
+            const SkPaint* paint, SaveFlags::Flags flags) {
+    const SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags));
+
+    int count = mCanvas->saveLayer(rec);
     recordPartialSave(flags);
     return count;
 }
 
 int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
-        int alpha, SkCanvas::SaveFlags flags) {
-    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
-    int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag);
-    recordPartialSave(flags);
-    return count;
+        int alpha, SaveFlags::Flags flags) {
+    SkTLazy<SkPaint> alphaPaint;
+    if (static_cast<unsigned>(alpha) < 0xFF) {
+        alphaPaint.init()->setAlpha(alpha);
+    }
+
+    return this->saveLayer(left, top, right, bottom, alphaPaint.getMaybeNull(),
+                           flags);
 }
 
 // ----------------------------------------------------------------------------
 // functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
 // ----------------------------------------------------------------------------
 
-void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) {
+void SkiaCanvas::recordPartialSave(SaveFlags::Flags flags) {
     // A partial save is a save operation which doesn't capture the full canvas state.
-    // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+    // (either SaveFlags::Matrix or SaveFlags::Clip is missing).
 
     // Mask-out non canvas state bits.
-    flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+    flags &= SaveFlags::MatrixClip;
 
-    if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+    if (flags == SaveFlags::MatrixClip) {
         // not a partial save.
         return;
     }
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 976f775..6530d4ed8 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -159,7 +159,21 @@
 }
 
 void SkiaCanvasProxy::willSave() {
-    mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+    mCanvas->save(android::SaveFlags::MatrixClip);
+}
+
+static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) {
+    SaveFlags::Flags saveFlags = 0;
+
+    if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) {
+        saveFlags |= SaveFlags::ClipToLayer;
+    }
+
+    if (!(layerFlags & SkCanvas::kIsOpaque_SaveLayerFlag)) {
+        saveFlags |= SaveFlags::HasAlphaLayer;
+    }
+
+    return saveFlags;
 }
 
 SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) {
@@ -170,7 +184,7 @@
         rect = SkRect::MakeEmpty();
     }
     mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
-                       (SkCanvas::SaveFlags) SaveLayerFlagsToSaveFlags(saveLayerRec.fSaveLayerFlags));
+                       saveFlags(saveLayerRec.fSaveLayerFlags));
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
 
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index c6d8977..27fea1f 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -16,7 +16,7 @@
 
 #include "Snapshot.h"
 
-#include <SkCanvas.h>
+#include "Canvas.h"
 
 namespace android {
 namespace uirenderer {
@@ -57,14 +57,14 @@
         , mClipArea(nullptr)
         , mViewportData(s->mViewportData)
         , mRelativeLightCenter(s->mRelativeLightCenter) {
-    if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
+    if (saveFlags & SaveFlags::Matrix) {
         mTransformRoot = *s->transform;
         transform = &mTransformRoot;
     } else {
         transform = s->transform;
     }
 
-    if (saveFlags & SkCanvas::kClip_SaveFlag) {
+    if (saveFlags & SaveFlags::Clip) {
         mClipAreaRoot = s->getClipArea();
         mClipArea = &mClipAreaRoot;
     } else {
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 5046d37..c09b6dd 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -97,7 +97,7 @@
 
 void Texture::upload(GLint internalformat, uint32_t width, uint32_t height,
         GLenum format, GLenum type, const void* pixels) {
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
     bool needsAlloc = updateSize(width, height, internalformat);
     if (!mId) {
         glGenTextures(1, &mId);
@@ -112,7 +112,7 @@
         glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
                 format, type, pixels);
     }
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
 }
 
 static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 3e20608..1d31c9e 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -420,7 +420,7 @@
         return;
     }
 
-    int saveCount = outCanvas->save(SkCanvas::SaveFlags::kMatrixClip_SaveFlag);
+    int saveCount = outCanvas->save(SaveFlags::MatrixClip);
     outCanvas->translate(mBounds.fLeft, mBounds.fTop);
 
     // Handle RTL mirroring.
diff --git a/libs/hwui/tests/common/nullegl.cpp b/libs/hwui/debug/nullegl.cpp
similarity index 100%
rename from libs/hwui/tests/common/nullegl.cpp
rename to libs/hwui/debug/nullegl.cpp
diff --git a/libs/hwui/tests/common/nullgles.cpp b/libs/hwui/debug/nullgles.cpp
similarity index 99%
rename from libs/hwui/tests/common/nullgles.cpp
rename to libs/hwui/debug/nullgles.cpp
index f8e8c98..ffb0649 100644
--- a/libs/hwui/tests/common/nullgles.cpp
+++ b/libs/hwui/debug/nullgles.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "unwrap_gles.h"
+
 #include <GLES3/gl3.h>
 #include <GLES2/gl2ext.h>
 
diff --git a/libs/hwui/debug/unwrap_gles.h b/libs/hwui/debug/unwrap_gles.h
new file mode 100644
index 0000000..7716a73
--- /dev/null
+++ b/libs/hwui/debug/unwrap_gles.h
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef HWUI_GLES_WRAP_ENABLED
+#undef HWUI_GLES_WRAP_ENABLED
+
+#undef glActiveShaderProgram
+#undef glActiveShaderProgramEXT
+#undef glActiveTexture
+#undef glAlphaFunc
+#undef glAlphaFuncQCOM
+#undef glAlphaFuncx
+#undef glAlphaFuncxOES
+#undef glApplyFramebufferAttachmentCMAAINTEL
+#undef glAttachShader
+#undef glBeginConditionalRenderNV
+#undef glBeginPerfMonitorAMD
+#undef glBeginPerfQueryINTEL
+#undef glBeginQuery
+#undef glBeginQueryEXT
+#undef glBeginTransformFeedback
+#undef glBindAttribLocation
+#undef glBindBuffer
+#undef glBindBufferBase
+#undef glBindBufferRange
+#undef glBindFragDataLocationEXT
+#undef glBindFragDataLocationIndexedEXT
+#undef glBindFramebuffer
+#undef glBindFramebufferOES
+#undef glBindImageTexture
+#undef glBindProgramPipeline
+#undef glBindProgramPipelineEXT
+#undef glBindRenderbuffer
+#undef glBindRenderbufferOES
+#undef glBindSampler
+#undef glBindTexture
+#undef glBindTransformFeedback
+#undef glBindVertexArray
+#undef glBindVertexArrayOES
+#undef glBindVertexBuffer
+#undef glBlendBarrier
+#undef glBlendBarrierKHR
+#undef glBlendBarrierNV
+#undef glBlendColor
+#undef glBlendEquation
+#undef glBlendEquationOES
+#undef glBlendEquationSeparate
+#undef glBlendEquationSeparateOES
+#undef glBlendEquationSeparatei
+#undef glBlendEquationSeparateiEXT
+#undef glBlendEquationSeparateiOES
+#undef glBlendEquationi
+#undef glBlendEquationiEXT
+#undef glBlendEquationiOES
+#undef glBlendFunc
+#undef glBlendFuncSeparate
+#undef glBlendFuncSeparateOES
+#undef glBlendFuncSeparatei
+#undef glBlendFuncSeparateiEXT
+#undef glBlendFuncSeparateiOES
+#undef glBlendFunci
+#undef glBlendFunciEXT
+#undef glBlendFunciOES
+#undef glBlendParameteriNV
+#undef glBlitFramebuffer
+#undef glBlitFramebufferANGLE
+#undef glBlitFramebufferNV
+#undef glBufferData
+#undef glBufferStorageEXT
+#undef glBufferSubData
+#undef glCheckFramebufferStatus
+#undef glCheckFramebufferStatusOES
+#undef glClear
+#undef glClearBufferfi
+#undef glClearBufferfv
+#undef glClearBufferiv
+#undef glClearBufferuiv
+#undef glClearColor
+#undef glClearColorx
+#undef glClearColorxOES
+#undef glClearDepthf
+#undef glClearDepthfOES
+#undef glClearDepthx
+#undef glClearDepthxOES
+#undef glClearStencil
+#undef glClientActiveTexture
+#undef glClientWaitSync
+#undef glClientWaitSyncAPPLE
+#undef glClipPlanef
+#undef glClipPlanefIMG
+#undef glClipPlanefOES
+#undef glClipPlanex
+#undef glClipPlanexIMG
+#undef glClipPlanexOES
+#undef glColor4f
+#undef glColor4ub
+#undef glColor4x
+#undef glColor4xOES
+#undef glColorMask
+#undef glColorMaski
+#undef glColorMaskiEXT
+#undef glColorMaskiOES
+#undef glColorPointer
+#undef glCompileShader
+#undef glCompressedTexImage2D
+#undef glCompressedTexImage3D
+#undef glCompressedTexImage3DOES
+#undef glCompressedTexSubImage2D
+#undef glCompressedTexSubImage3D
+#undef glCompressedTexSubImage3DOES
+#undef glCopyBufferSubData
+#undef glCopyBufferSubDataNV
+#undef glCopyImageSubData
+#undef glCopyImageSubDataEXT
+#undef glCopyImageSubDataOES
+#undef glCopyPathNV
+#undef glCopyTexImage2D
+#undef glCopyTexSubImage2D
+#undef glCopyTexSubImage3D
+#undef glCopyTexSubImage3DOES
+#undef glCopyTextureLevelsAPPLE
+#undef glCoverFillPathInstancedNV
+#undef glCoverFillPathNV
+#undef glCoverStrokePathInstancedNV
+#undef glCoverStrokePathNV
+#undef glCoverageMaskNV
+#undef glCoverageModulationNV
+#undef glCoverageModulationTableNV
+#undef glCoverageOperationNV
+#undef glCreatePerfQueryINTEL
+#undef glCreateProgram
+#undef glCreateShader
+#undef glCreateShaderProgramv
+#undef glCreateShaderProgramvEXT
+#undef glCullFace
+#undef glCurrentPaletteMatrixOES
+#undef glDebugMessageCallback
+#undef glDebugMessageCallbackKHR
+#undef glDebugMessageControl
+#undef glDebugMessageControlKHR
+#undef glDebugMessageInsert
+#undef glDebugMessageInsertKHR
+#undef glDeleteBuffers
+#undef glDeleteFencesNV
+#undef glDeleteFramebuffers
+#undef glDeleteFramebuffersOES
+#undef glDeletePathsNV
+#undef glDeletePerfMonitorsAMD
+#undef glDeletePerfQueryINTEL
+#undef glDeleteProgram
+#undef glDeleteProgramPipelines
+#undef glDeleteProgramPipelinesEXT
+#undef glDeleteQueries
+#undef glDeleteQueriesEXT
+#undef glDeleteRenderbuffers
+#undef glDeleteRenderbuffersOES
+#undef glDeleteSamplers
+#undef glDeleteShader
+#undef glDeleteSync
+#undef glDeleteSyncAPPLE
+#undef glDeleteTextures
+#undef glDeleteTransformFeedbacks
+#undef glDeleteVertexArrays
+#undef glDeleteVertexArraysOES
+#undef glDepthFunc
+#undef glDepthMask
+#undef glDepthRangeArrayfvNV
+#undef glDepthRangeIndexedfNV
+#undef glDepthRangef
+#undef glDepthRangefOES
+#undef glDepthRangex
+#undef glDepthRangexOES
+#undef glDetachShader
+#undef glDisable
+#undef glDisableClientState
+#undef glDisableDriverControlQCOM
+#undef glDisableVertexAttribArray
+#undef glDisablei
+#undef glDisableiEXT
+#undef glDisableiNV
+#undef glDisableiOES
+#undef glDiscardFramebufferEXT
+#undef glDispatchCompute
+#undef glDispatchComputeIndirect
+#undef glDrawArrays
+#undef glDrawArraysIndirect
+#undef glDrawArraysInstanced
+#undef glDrawArraysInstancedANGLE
+#undef glDrawArraysInstancedBaseInstanceEXT
+#undef glDrawArraysInstancedEXT
+#undef glDrawArraysInstancedNV
+#undef glDrawBuffers
+#undef glDrawBuffersEXT
+#undef glDrawBuffersIndexedEXT
+#undef glDrawBuffersNV
+#undef glDrawElements
+#undef glDrawElementsBaseVertex
+#undef glDrawElementsBaseVertexEXT
+#undef glDrawElementsBaseVertexOES
+#undef glDrawElementsIndirect
+#undef glDrawElementsInstanced
+#undef glDrawElementsInstancedANGLE
+#undef glDrawElementsInstancedBaseInstanceEXT
+#undef glDrawElementsInstancedBaseVertex
+#undef glDrawElementsInstancedBaseVertexBaseInstanceEXT
+#undef glDrawElementsInstancedBaseVertexEXT
+#undef glDrawElementsInstancedBaseVertexOES
+#undef glDrawElementsInstancedEXT
+#undef glDrawElementsInstancedNV
+#undef glDrawRangeElements
+#undef glDrawRangeElementsBaseVertex
+#undef glDrawRangeElementsBaseVertexEXT
+#undef glDrawRangeElementsBaseVertexOES
+#undef glDrawTexfOES
+#undef glDrawTexfvOES
+#undef glDrawTexiOES
+#undef glDrawTexivOES
+#undef glDrawTexsOES
+#undef glDrawTexsvOES
+#undef glDrawTexxOES
+#undef glDrawTexxvOES
+#undef glEGLImageTargetRenderbufferStorageOES
+#undef glEGLImageTargetTexture2DOES
+#undef glEnable
+#undef glEnableClientState
+#undef glEnableDriverControlQCOM
+#undef glEnableVertexAttribArray
+#undef glEnablei
+#undef glEnableiEXT
+#undef glEnableiNV
+#undef glEnableiOES
+#undef glEndConditionalRenderNV
+#undef glEndPerfMonitorAMD
+#undef glEndPerfQueryINTEL
+#undef glEndQuery
+#undef glEndQueryEXT
+#undef glEndTilingQCOM
+#undef glEndTransformFeedback
+#undef glExtGetBufferPointervQCOM
+#undef glExtGetBuffersQCOM
+#undef glExtGetFramebuffersQCOM
+#undef glExtGetProgramBinarySourceQCOM
+#undef glExtGetProgramsQCOM
+#undef glExtGetRenderbuffersQCOM
+#undef glExtGetShadersQCOM
+#undef glExtGetTexLevelParameterivQCOM
+#undef glExtGetTexSubImageQCOM
+#undef glExtGetTexturesQCOM
+#undef glExtIsProgramBinaryQCOM
+#undef glExtTexObjectStateOverrideiQCOM
+#undef glFenceSync
+#undef glFenceSyncAPPLE
+#undef glFinish
+#undef glFinishFenceNV
+#undef glFlush
+#undef glFlushMappedBufferRange
+#undef glFlushMappedBufferRangeEXT
+#undef glFogf
+#undef glFogfv
+#undef glFogx
+#undef glFogxOES
+#undef glFogxv
+#undef glFogxvOES
+#undef glFragmentCoverageColorNV
+#undef glFramebufferParameteri
+#undef glFramebufferRenderbuffer
+#undef glFramebufferRenderbufferOES
+#undef glFramebufferSampleLocationsfvNV
+#undef glFramebufferTexture
+#undef glFramebufferTexture2D
+#undef glFramebufferTexture2DMultisampleEXT
+#undef glFramebufferTexture2DMultisampleIMG
+#undef glFramebufferTexture2DOES
+#undef glFramebufferTexture3DOES
+#undef glFramebufferTextureEXT
+#undef glFramebufferTextureLayer
+#undef glFramebufferTextureMultisampleMultiviewOVR
+#undef glFramebufferTextureMultiviewOVR
+#undef glFramebufferTextureOES
+#undef glFrontFace
+#undef glFrustumf
+#undef glFrustumfOES
+#undef glFrustumx
+#undef glFrustumxOES
+#undef glGenBuffers
+#undef glGenFencesNV
+#undef glGenFramebuffers
+#undef glGenFramebuffersOES
+#undef glGenPathsNV
+#undef glGenPerfMonitorsAMD
+#undef glGenProgramPipelines
+#undef glGenProgramPipelinesEXT
+#undef glGenQueries
+#undef glGenQueriesEXT
+#undef glGenRenderbuffers
+#undef glGenRenderbuffersOES
+#undef glGenSamplers
+#undef glGenTextures
+#undef glGenTransformFeedbacks
+#undef glGenVertexArrays
+#undef glGenVertexArraysOES
+#undef glGenerateMipmap
+#undef glGenerateMipmapOES
+#undef glGetActiveAttrib
+#undef glGetActiveUniform
+#undef glGetActiveUniformBlockName
+#undef glGetActiveUniformBlockiv
+#undef glGetActiveUniformsiv
+#undef glGetAttachedShaders
+#undef glGetAttribLocation
+#undef glGetBooleani_v
+#undef glGetBooleanv
+#undef glGetBufferParameteri64v
+#undef glGetBufferParameteriv
+#undef glGetBufferPointerv
+#undef glGetBufferPointervOES
+#undef glGetClipPlanef
+#undef glGetClipPlanefOES
+#undef glGetClipPlanex
+#undef glGetClipPlanexOES
+#undef glGetCoverageModulationTableNV
+#undef glGetDebugMessageLog
+#undef glGetDebugMessageLogKHR
+#undef glGetDriverControlStringQCOM
+#undef glGetDriverControlsQCOM
+#undef glGetError
+#undef glGetFenceivNV
+#undef glGetFirstPerfQueryIdINTEL
+#undef glGetFixedv
+#undef glGetFixedvOES
+#undef glGetFloati_vNV
+#undef glGetFloatv
+#undef glGetFragDataIndexEXT
+#undef glGetFragDataLocation
+#undef glGetFramebufferAttachmentParameteriv
+#undef glGetFramebufferAttachmentParameterivOES
+#undef glGetFramebufferParameteriv
+#undef glGetGraphicsResetStatus
+#undef glGetGraphicsResetStatusEXT
+#undef glGetGraphicsResetStatusKHR
+#undef glGetImageHandleNV
+#undef glGetInteger64i_v
+#undef glGetInteger64v
+#undef glGetInteger64vAPPLE
+#undef glGetIntegeri_v
+#undef glGetIntegeri_vEXT
+#undef glGetIntegerv
+#undef glGetInternalformatSampleivNV
+#undef glGetInternalformativ
+#undef glGetLightfv
+#undef glGetLightxv
+#undef glGetLightxvOES
+#undef glGetMaterialfv
+#undef glGetMaterialxv
+#undef glGetMaterialxvOES
+#undef glGetMultisamplefv
+#undef glGetNextPerfQueryIdINTEL
+#undef glGetObjectLabel
+#undef glGetObjectLabelEXT
+#undef glGetObjectLabelKHR
+#undef glGetObjectPtrLabel
+#undef glGetObjectPtrLabelKHR
+#undef glGetPathCommandsNV
+#undef glGetPathCoordsNV
+#undef glGetPathDashArrayNV
+#undef glGetPathLengthNV
+#undef glGetPathMetricRangeNV
+#undef glGetPathMetricsNV
+#undef glGetPathParameterfvNV
+#undef glGetPathParameterivNV
+#undef glGetPathSpacingNV
+#undef glGetPerfCounterInfoINTEL
+#undef glGetPerfMonitorCounterDataAMD
+#undef glGetPerfMonitorCounterInfoAMD
+#undef glGetPerfMonitorCounterStringAMD
+#undef glGetPerfMonitorCountersAMD
+#undef glGetPerfMonitorGroupStringAMD
+#undef glGetPerfMonitorGroupsAMD
+#undef glGetPerfQueryDataINTEL
+#undef glGetPerfQueryIdByNameINTEL
+#undef glGetPerfQueryInfoINTEL
+#undef glGetPointerv
+#undef glGetPointervKHR
+#undef glGetProgramBinary
+#undef glGetProgramBinaryOES
+#undef glGetProgramInfoLog
+#undef glGetProgramInterfaceiv
+#undef glGetProgramPipelineInfoLog
+#undef glGetProgramPipelineInfoLogEXT
+#undef glGetProgramPipelineiv
+#undef glGetProgramPipelineivEXT
+#undef glGetProgramResourceIndex
+#undef glGetProgramResourceLocation
+#undef glGetProgramResourceLocationIndexEXT
+#undef glGetProgramResourceName
+#undef glGetProgramResourcefvNV
+#undef glGetProgramResourceiv
+#undef glGetProgramiv
+#undef glGetQueryObjecti64vEXT
+#undef glGetQueryObjectivEXT
+#undef glGetQueryObjectui64vEXT
+#undef glGetQueryObjectuiv
+#undef glGetQueryObjectuivEXT
+#undef glGetQueryiv
+#undef glGetQueryivEXT
+#undef glGetRenderbufferParameteriv
+#undef glGetRenderbufferParameterivOES
+#undef glGetSamplerParameterIiv
+#undef glGetSamplerParameterIivEXT
+#undef glGetSamplerParameterIivOES
+#undef glGetSamplerParameterIuiv
+#undef glGetSamplerParameterIuivEXT
+#undef glGetSamplerParameterIuivOES
+#undef glGetSamplerParameterfv
+#undef glGetSamplerParameteriv
+#undef glGetShaderInfoLog
+#undef glGetShaderPrecisionFormat
+#undef glGetShaderSource
+#undef glGetShaderiv
+#undef glGetString
+#undef glGetStringi
+#undef glGetSynciv
+#undef glGetSyncivAPPLE
+#undef glGetTexEnvfv
+#undef glGetTexEnviv
+#undef glGetTexEnvxv
+#undef glGetTexEnvxvOES
+#undef glGetTexGenfvOES
+#undef glGetTexGenivOES
+#undef glGetTexGenxvOES
+#undef glGetTexLevelParameterfv
+#undef glGetTexLevelParameteriv
+#undef glGetTexParameterIiv
+#undef glGetTexParameterIivEXT
+#undef glGetTexParameterIivOES
+#undef glGetTexParameterIuiv
+#undef glGetTexParameterIuivEXT
+#undef glGetTexParameterIuivOES
+#undef glGetTexParameterfv
+#undef glGetTexParameteriv
+#undef glGetTexParameterxv
+#undef glGetTexParameterxvOES
+#undef glGetTextureHandleNV
+#undef glGetTextureSamplerHandleNV
+#undef glGetTransformFeedbackVarying
+#undef glGetTranslatedShaderSourceANGLE
+#undef glGetUniformBlockIndex
+#undef glGetUniformIndices
+#undef glGetUniformLocation
+#undef glGetUniformfv
+#undef glGetUniformiv
+#undef glGetUniformuiv
+#undef glGetVertexAttribIiv
+#undef glGetVertexAttribIuiv
+#undef glGetVertexAttribPointerv
+#undef glGetVertexAttribfv
+#undef glGetVertexAttribiv
+#undef glGetnUniformfv
+#undef glGetnUniformfvEXT
+#undef glGetnUniformfvKHR
+#undef glGetnUniformiv
+#undef glGetnUniformivEXT
+#undef glGetnUniformivKHR
+#undef glGetnUniformuiv
+#undef glGetnUniformuivKHR
+#undef glHint
+#undef glInsertEventMarkerEXT
+#undef glInterpolatePathsNV
+#undef glInvalidateFramebuffer
+#undef glInvalidateSubFramebuffer
+#undef glIsBuffer
+#undef glIsEnabled
+#undef glIsEnabledi
+#undef glIsEnablediEXT
+#undef glIsEnablediNV
+#undef glIsEnablediOES
+#undef glIsFenceNV
+#undef glIsFramebuffer
+#undef glIsFramebufferOES
+#undef glIsImageHandleResidentNV
+#undef glIsPathNV
+#undef glIsPointInFillPathNV
+#undef glIsPointInStrokePathNV
+#undef glIsProgram
+#undef glIsProgramPipeline
+#undef glIsProgramPipelineEXT
+#undef glIsQuery
+#undef glIsQueryEXT
+#undef glIsRenderbuffer
+#undef glIsRenderbufferOES
+#undef glIsSampler
+#undef glIsShader
+#undef glIsSync
+#undef glIsSyncAPPLE
+#undef glIsTexture
+#undef glIsTextureHandleResidentNV
+#undef glIsTransformFeedback
+#undef glIsVertexArray
+#undef glIsVertexArrayOES
+#undef glLabelObjectEXT
+#undef glLightModelf
+#undef glLightModelfv
+#undef glLightModelx
+#undef glLightModelxOES
+#undef glLightModelxv
+#undef glLightModelxvOES
+#undef glLightf
+#undef glLightfv
+#undef glLightx
+#undef glLightxOES
+#undef glLightxv
+#undef glLightxvOES
+#undef glLineWidth
+#undef glLineWidthx
+#undef glLineWidthxOES
+#undef glLinkProgram
+#undef glLoadIdentity
+#undef glLoadMatrixf
+#undef glLoadMatrixx
+#undef glLoadMatrixxOES
+#undef glLoadPaletteFromModelViewMatrixOES
+#undef glLogicOp
+#undef glMakeImageHandleNonResidentNV
+#undef glMakeImageHandleResidentNV
+#undef glMakeTextureHandleNonResidentNV
+#undef glMakeTextureHandleResidentNV
+#undef glMapBufferOES
+#undef glMapBufferRange
+#undef glMapBufferRangeEXT
+#undef glMaterialf
+#undef glMaterialfv
+#undef glMaterialx
+#undef glMaterialxOES
+#undef glMaterialxv
+#undef glMaterialxvOES
+#undef glMatrixIndexPointerOES
+#undef glMatrixLoad3x2fNV
+#undef glMatrixLoad3x3fNV
+#undef glMatrixLoadTranspose3x3fNV
+#undef glMatrixMode
+#undef glMatrixMult3x2fNV
+#undef glMatrixMult3x3fNV
+#undef glMatrixMultTranspose3x3fNV
+#undef glMemoryBarrier
+#undef glMemoryBarrierByRegion
+#undef glMinSampleShading
+#undef glMinSampleShadingOES
+#undef glMultMatrixf
+#undef glMultMatrixx
+#undef glMultMatrixxOES
+#undef glMultiDrawArraysEXT
+#undef glMultiDrawArraysIndirectEXT
+#undef glMultiDrawElementsBaseVertexEXT
+#undef glMultiDrawElementsBaseVertexOES
+#undef glMultiDrawElementsEXT
+#undef glMultiDrawElementsIndirectEXT
+#undef glMultiTexCoord4f
+#undef glMultiTexCoord4x
+#undef glMultiTexCoord4xOES
+#undef glNamedFramebufferSampleLocationsfvNV
+#undef glNormal3f
+#undef glNormal3x
+#undef glNormal3xOES
+#undef glNormalPointer
+#undef glObjectLabel
+#undef glObjectLabelKHR
+#undef glObjectPtrLabel
+#undef glObjectPtrLabelKHR
+#undef glOrthof
+#undef glOrthofOES
+#undef glOrthox
+#undef glOrthoxOES
+#undef glPatchParameteri
+#undef glPatchParameteriEXT
+#undef glPatchParameteriOES
+#undef glPathCommandsNV
+#undef glPathCoordsNV
+#undef glPathCoverDepthFuncNV
+#undef glPathDashArrayNV
+#undef glPathGlyphIndexArrayNV
+#undef glPathGlyphIndexRangeNV
+#undef glPathGlyphRangeNV
+#undef glPathGlyphsNV
+#undef glPathMemoryGlyphIndexArrayNV
+#undef glPathParameterfNV
+#undef glPathParameterfvNV
+#undef glPathParameteriNV
+#undef glPathParameterivNV
+#undef glPathStencilDepthOffsetNV
+#undef glPathStencilFuncNV
+#undef glPathStringNV
+#undef glPathSubCommandsNV
+#undef glPathSubCoordsNV
+#undef glPauseTransformFeedback
+#undef glPixelStorei
+#undef glPointAlongPathNV
+#undef glPointParameterf
+#undef glPointParameterfv
+#undef glPointParameterx
+#undef glPointParameterxOES
+#undef glPointParameterxv
+#undef glPointParameterxvOES
+#undef glPointSize
+#undef glPointSizePointerOES
+#undef glPointSizex
+#undef glPointSizexOES
+#undef glPolygonModeNV
+#undef glPolygonOffset
+#undef glPolygonOffsetx
+#undef glPolygonOffsetxOES
+#undef glPopDebugGroup
+#undef glPopDebugGroupKHR
+#undef glPopGroupMarkerEXT
+#undef glPopMatrix
+#undef glPrimitiveBoundingBox
+#undef glPrimitiveBoundingBoxEXT
+#undef glPrimitiveBoundingBoxOES
+#undef glProgramBinary
+#undef glProgramBinaryOES
+#undef glProgramParameteri
+#undef glProgramParameteriEXT
+#undef glProgramPathFragmentInputGenNV
+#undef glProgramUniform1f
+#undef glProgramUniform1fEXT
+#undef glProgramUniform1fv
+#undef glProgramUniform1fvEXT
+#undef glProgramUniform1i
+#undef glProgramUniform1iEXT
+#undef glProgramUniform1iv
+#undef glProgramUniform1ivEXT
+#undef glProgramUniform1ui
+#undef glProgramUniform1uiEXT
+#undef glProgramUniform1uiv
+#undef glProgramUniform1uivEXT
+#undef glProgramUniform2f
+#undef glProgramUniform2fEXT
+#undef glProgramUniform2fv
+#undef glProgramUniform2fvEXT
+#undef glProgramUniform2i
+#undef glProgramUniform2iEXT
+#undef glProgramUniform2iv
+#undef glProgramUniform2ivEXT
+#undef glProgramUniform2ui
+#undef glProgramUniform2uiEXT
+#undef glProgramUniform2uiv
+#undef glProgramUniform2uivEXT
+#undef glProgramUniform3f
+#undef glProgramUniform3fEXT
+#undef glProgramUniform3fv
+#undef glProgramUniform3fvEXT
+#undef glProgramUniform3i
+#undef glProgramUniform3iEXT
+#undef glProgramUniform3iv
+#undef glProgramUniform3ivEXT
+#undef glProgramUniform3ui
+#undef glProgramUniform3uiEXT
+#undef glProgramUniform3uiv
+#undef glProgramUniform3uivEXT
+#undef glProgramUniform4f
+#undef glProgramUniform4fEXT
+#undef glProgramUniform4fv
+#undef glProgramUniform4fvEXT
+#undef glProgramUniform4i
+#undef glProgramUniform4iEXT
+#undef glProgramUniform4iv
+#undef glProgramUniform4ivEXT
+#undef glProgramUniform4ui
+#undef glProgramUniform4uiEXT
+#undef glProgramUniform4uiv
+#undef glProgramUniform4uivEXT
+#undef glProgramUniformHandleui64NV
+#undef glProgramUniformHandleui64vNV
+#undef glProgramUniformMatrix2fv
+#undef glProgramUniformMatrix2fvEXT
+#undef glProgramUniformMatrix2x3fv
+#undef glProgramUniformMatrix2x3fvEXT
+#undef glProgramUniformMatrix2x4fv
+#undef glProgramUniformMatrix2x4fvEXT
+#undef glProgramUniformMatrix3fv
+#undef glProgramUniformMatrix3fvEXT
+#undef glProgramUniformMatrix3x2fv
+#undef glProgramUniformMatrix3x2fvEXT
+#undef glProgramUniformMatrix3x4fv
+#undef glProgramUniformMatrix3x4fvEXT
+#undef glProgramUniformMatrix4fv
+#undef glProgramUniformMatrix4fvEXT
+#undef glProgramUniformMatrix4x2fv
+#undef glProgramUniformMatrix4x2fvEXT
+#undef glProgramUniformMatrix4x3fv
+#undef glProgramUniformMatrix4x3fvEXT
+#undef glPushDebugGroup
+#undef glPushDebugGroupKHR
+#undef glPushGroupMarkerEXT
+#undef glPushMatrix
+#undef glQueryCounterEXT
+#undef glQueryMatrixxOES
+#undef glRasterSamplesEXT
+#undef glReadBuffer
+#undef glReadBufferIndexedEXT
+#undef glReadBufferNV
+#undef glReadPixels
+#undef glReadnPixels
+#undef glReadnPixelsEXT
+#undef glReadnPixelsKHR
+#undef glReleaseShaderCompiler
+#undef glRenderbufferStorage
+#undef glRenderbufferStorageMultisample
+#undef glRenderbufferStorageMultisampleANGLE
+#undef glRenderbufferStorageMultisampleAPPLE
+#undef glRenderbufferStorageMultisampleEXT
+#undef glRenderbufferStorageMultisampleIMG
+#undef glRenderbufferStorageMultisampleNV
+#undef glRenderbufferStorageOES
+#undef glResolveDepthValuesNV
+#undef glResolveMultisampleFramebufferAPPLE
+#undef glResumeTransformFeedback
+#undef glRotatef
+#undef glRotatex
+#undef glRotatexOES
+#undef glSampleCoverage
+#undef glSampleCoveragex
+#undef glSampleCoveragexOES
+#undef glSampleMaski
+#undef glSamplerParameterIiv
+#undef glSamplerParameterIivEXT
+#undef glSamplerParameterIivOES
+#undef glSamplerParameterIuiv
+#undef glSamplerParameterIuivEXT
+#undef glSamplerParameterIuivOES
+#undef glSamplerParameterf
+#undef glSamplerParameterfv
+#undef glSamplerParameteri
+#undef glSamplerParameteriv
+#undef glScalef
+#undef glScalex
+#undef glScalexOES
+#undef glScissor
+#undef glScissorArrayvNV
+#undef glScissorIndexedNV
+#undef glScissorIndexedvNV
+#undef glSelectPerfMonitorCountersAMD
+#undef glSetFenceNV
+#undef glShadeModel
+#undef glShaderBinary
+#undef glShaderSource
+#undef glStartTilingQCOM
+#undef glStencilFillPathInstancedNV
+#undef glStencilFillPathNV
+#undef glStencilFunc
+#undef glStencilFuncSeparate
+#undef glStencilMask
+#undef glStencilMaskSeparate
+#undef glStencilOp
+#undef glStencilOpSeparate
+#undef glStencilStrokePathInstancedNV
+#undef glStencilStrokePathNV
+#undef glStencilThenCoverFillPathInstancedNV
+#undef glStencilThenCoverFillPathNV
+#undef glStencilThenCoverStrokePathInstancedNV
+#undef glStencilThenCoverStrokePathNV
+#undef glSubpixelPrecisionBiasNV
+#undef glTestFenceNV
+#undef glTexBuffer
+#undef glTexBufferEXT
+#undef glTexBufferOES
+#undef glTexBufferRange
+#undef glTexBufferRangeEXT
+#undef glTexBufferRangeOES
+#undef glTexCoordPointer
+#undef glTexEnvf
+#undef glTexEnvfv
+#undef glTexEnvi
+#undef glTexEnviv
+#undef glTexEnvx
+#undef glTexEnvxOES
+#undef glTexEnvxv
+#undef glTexEnvxvOES
+#undef glTexGenfOES
+#undef glTexGenfvOES
+#undef glTexGeniOES
+#undef glTexGenivOES
+#undef glTexGenxOES
+#undef glTexGenxvOES
+#undef glTexImage2D
+#undef glTexImage3D
+#undef glTexImage3DOES
+#undef glTexPageCommitmentEXT
+#undef glTexParameterIiv
+#undef glTexParameterIivEXT
+#undef glTexParameterIivOES
+#undef glTexParameterIuiv
+#undef glTexParameterIuivEXT
+#undef glTexParameterIuivOES
+#undef glTexParameterf
+#undef glTexParameterfv
+#undef glTexParameteri
+#undef glTexParameteriv
+#undef glTexParameterx
+#undef glTexParameterxOES
+#undef glTexParameterxv
+#undef glTexParameterxvOES
+#undef glTexStorage1DEXT
+#undef glTexStorage2D
+#undef glTexStorage2DEXT
+#undef glTexStorage2DMultisample
+#undef glTexStorage3D
+#undef glTexStorage3DEXT
+#undef glTexStorage3DMultisample
+#undef glTexStorage3DMultisampleOES
+#undef glTexSubImage2D
+#undef glTexSubImage3D
+#undef glTexSubImage3DOES
+#undef glTextureStorage1DEXT
+#undef glTextureStorage2DEXT
+#undef glTextureStorage3DEXT
+#undef glTextureViewEXT
+#undef glTextureViewOES
+#undef glTransformFeedbackVaryings
+#undef glTransformPathNV
+#undef glTranslatef
+#undef glTranslatex
+#undef glTranslatexOES
+#undef glUniform1f
+#undef glUniform1fv
+#undef glUniform1i
+#undef glUniform1iv
+#undef glUniform1ui
+#undef glUniform1uiv
+#undef glUniform2f
+#undef glUniform2fv
+#undef glUniform2i
+#undef glUniform2iv
+#undef glUniform2ui
+#undef glUniform2uiv
+#undef glUniform3f
+#undef glUniform3fv
+#undef glUniform3i
+#undef glUniform3iv
+#undef glUniform3ui
+#undef glUniform3uiv
+#undef glUniform4f
+#undef glUniform4fv
+#undef glUniform4i
+#undef glUniform4iv
+#undef glUniform4ui
+#undef glUniform4uiv
+#undef glUniformBlockBinding
+#undef glUniformHandleui64NV
+#undef glUniformHandleui64vNV
+#undef glUniformMatrix2fv
+#undef glUniformMatrix2x3fv
+#undef glUniformMatrix2x3fvNV
+#undef glUniformMatrix2x4fv
+#undef glUniformMatrix2x4fvNV
+#undef glUniformMatrix3fv
+#undef glUniformMatrix3x2fv
+#undef glUniformMatrix3x2fvNV
+#undef glUniformMatrix3x4fv
+#undef glUniformMatrix3x4fvNV
+#undef glUniformMatrix4fv
+#undef glUniformMatrix4x2fv
+#undef glUniformMatrix4x2fvNV
+#undef glUniformMatrix4x3fv
+#undef glUniformMatrix4x3fvNV
+#undef glUnmapBuffer
+#undef glUnmapBufferOES
+#undef glUseProgram
+#undef glUseProgramStages
+#undef glUseProgramStagesEXT
+#undef glValidateProgram
+#undef glValidateProgramPipeline
+#undef glValidateProgramPipelineEXT
+#undef glVertexAttrib1f
+#undef glVertexAttrib1fv
+#undef glVertexAttrib2f
+#undef glVertexAttrib2fv
+#undef glVertexAttrib3f
+#undef glVertexAttrib3fv
+#undef glVertexAttrib4f
+#undef glVertexAttrib4fv
+#undef glVertexAttribBinding
+#undef glVertexAttribDivisor
+#undef glVertexAttribDivisorANGLE
+#undef glVertexAttribDivisorEXT
+#undef glVertexAttribDivisorNV
+#undef glVertexAttribFormat
+#undef glVertexAttribI4i
+#undef glVertexAttribI4iv
+#undef glVertexAttribI4ui
+#undef glVertexAttribI4uiv
+#undef glVertexAttribIFormat
+#undef glVertexAttribIPointer
+#undef glVertexAttribPointer
+#undef glVertexBindingDivisor
+#undef glVertexPointer
+#undef glViewport
+#undef glViewportArrayvNV
+#undef glViewportIndexedfNV
+#undef glViewportIndexedfvNV
+#undef glWaitSync
+#undef glWaitSyncAPPLE
+#undef glWeightPathsNV
+#undef glWeightPointerOES
+
+#endif // HWUI_GLES_WRAP_ENABLED
diff --git a/libs/hwui/debug/wrap_gles.cpp b/libs/hwui/debug/wrap_gles.cpp
new file mode 100644
index 0000000..c4f2e35
--- /dev/null
+++ b/libs/hwui/debug/wrap_gles.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unwrap_gles.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl31.h>
+#include <GLES3/gl32.h>
+
+#include <cutils/log.h>
+
+void assertNoGlErrors(const char* apicall) {
+    GLenum status = GL_NO_ERROR;
+    GLenum lastError = GL_NO_ERROR;
+    const char* lastErrorName = nullptr;
+    while ((status = glGetError()) != GL_NO_ERROR) {
+        lastError = status;
+        switch (status) {
+        case GL_INVALID_ENUM:
+            ALOGE("GL error:  GL_INVALID_ENUM");
+            lastErrorName = "GL_INVALID_ENUM";
+            break;
+        case GL_INVALID_VALUE:
+            ALOGE("GL error:  GL_INVALID_VALUE");
+            lastErrorName = "GL_INVALID_VALUE";
+            break;
+        case GL_INVALID_OPERATION:
+            ALOGE("GL error:  GL_INVALID_OPERATION");
+            lastErrorName = "GL_INVALID_OPERATION";
+            break;
+        case GL_OUT_OF_MEMORY:
+            ALOGE("GL error:  Out of memory!");
+            lastErrorName = "GL_OUT_OF_MEMORY";
+            break;
+        default:
+            ALOGE("GL error: 0x%x", status);
+            lastErrorName = "UNKNOWN";
+        }
+    }
+    LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR,
+            "%s error! %s (0x%x)", apicall, lastErrorName, lastError);
+}
+
+#define API_ENTRY(x) wrap_##x
+#define CALL_GL_API(x, ...) x(__VA_ARGS__); assertNoGlErrors(#x)
+#define CALL_GL_API_RETURN(x, ...) auto ret = x(__VA_ARGS__);\
+    assertNoGlErrors(#x);\
+    return ret
+
+extern "C" {
+#include <gl2_api.in>
+#include <gl2ext_api.in>
+
+// libGLESv2 handles these specially, so they are not in gl2_api.in
+
+void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) {
+    CALL_GL_API(glGetBooleanv, pname, data);
+}
+void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) {
+    CALL_GL_API(glGetFloatv, pname, data);
+}
+void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) {
+    CALL_GL_API(glGetIntegerv, pname, data);
+}
+const GLubyte * API_ENTRY(glGetString)(GLenum name) {
+    CALL_GL_API_RETURN(glGetString, name);
+}
+const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) {
+    CALL_GL_API_RETURN(glGetStringi, name, index);
+}
+void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) {
+    CALL_GL_API(glGetInteger64v, pname, data);
+}
+}
diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h
new file mode 100644
index 0000000..4a35374
--- /dev/null
+++ b/libs/hwui/debug/wrap_gles.h
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HWUI_GLES_WRAP_ENABLED
+#define HWUI_GLES_WRAP_ENABLED
+
+#define glActiveShaderProgram wrap_glActiveShaderProgram
+#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT
+#define glActiveTexture wrap_glActiveTexture
+#define glAlphaFunc wrap_glAlphaFunc
+#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM
+#define glAlphaFuncx wrap_glAlphaFuncx
+#define glAlphaFuncxOES wrap_glAlphaFuncxOES
+#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL
+#define glAttachShader wrap_glAttachShader
+#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV
+#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD
+#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL
+#define glBeginQuery wrap_glBeginQuery
+#define glBeginQueryEXT wrap_glBeginQueryEXT
+#define glBeginTransformFeedback wrap_glBeginTransformFeedback
+#define glBindAttribLocation wrap_glBindAttribLocation
+#define glBindBuffer wrap_glBindBuffer
+#define glBindBufferBase wrap_glBindBufferBase
+#define glBindBufferRange wrap_glBindBufferRange
+#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT
+#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT
+#define glBindFramebuffer wrap_glBindFramebuffer
+#define glBindFramebufferOES wrap_glBindFramebufferOES
+#define glBindImageTexture wrap_glBindImageTexture
+#define glBindProgramPipeline wrap_glBindProgramPipeline
+#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT
+#define glBindRenderbuffer wrap_glBindRenderbuffer
+#define glBindRenderbufferOES wrap_glBindRenderbufferOES
+#define glBindSampler wrap_glBindSampler
+#define glBindTexture wrap_glBindTexture
+#define glBindTransformFeedback wrap_glBindTransformFeedback
+#define glBindVertexArray wrap_glBindVertexArray
+#define glBindVertexArrayOES wrap_glBindVertexArrayOES
+#define glBindVertexBuffer wrap_glBindVertexBuffer
+#define glBlendBarrier wrap_glBlendBarrier
+#define glBlendBarrierKHR wrap_glBlendBarrierKHR
+#define glBlendBarrierNV wrap_glBlendBarrierNV
+#define glBlendColor wrap_glBlendColor
+#define glBlendEquation wrap_glBlendEquation
+#define glBlendEquationOES wrap_glBlendEquationOES
+#define glBlendEquationSeparate wrap_glBlendEquationSeparate
+#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES
+#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei
+#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT
+#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES
+#define glBlendEquationi wrap_glBlendEquationi
+#define glBlendEquationiEXT wrap_glBlendEquationiEXT
+#define glBlendEquationiOES wrap_glBlendEquationiOES
+#define glBlendFunc wrap_glBlendFunc
+#define glBlendFuncSeparate wrap_glBlendFuncSeparate
+#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES
+#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei
+#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT
+#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES
+#define glBlendFunci wrap_glBlendFunci
+#define glBlendFunciEXT wrap_glBlendFunciEXT
+#define glBlendFunciOES wrap_glBlendFunciOES
+#define glBlendParameteriNV wrap_glBlendParameteriNV
+#define glBlitFramebuffer wrap_glBlitFramebuffer
+#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE
+#define glBlitFramebufferNV wrap_glBlitFramebufferNV
+#define glBufferData wrap_glBufferData
+#define glBufferStorageEXT wrap_glBufferStorageEXT
+#define glBufferSubData wrap_glBufferSubData
+#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus
+#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES
+#define glClear wrap_glClear
+#define glClearBufferfi wrap_glClearBufferfi
+#define glClearBufferfv wrap_glClearBufferfv
+#define glClearBufferiv wrap_glClearBufferiv
+#define glClearBufferuiv wrap_glClearBufferuiv
+#define glClearColor wrap_glClearColor
+#define glClearColorx wrap_glClearColorx
+#define glClearColorxOES wrap_glClearColorxOES
+#define glClearDepthf wrap_glClearDepthf
+#define glClearDepthfOES wrap_glClearDepthfOES
+#define glClearDepthx wrap_glClearDepthx
+#define glClearDepthxOES wrap_glClearDepthxOES
+#define glClearStencil wrap_glClearStencil
+#define glClientActiveTexture wrap_glClientActiveTexture
+#define glClientWaitSync wrap_glClientWaitSync
+#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE
+#define glClipPlanef wrap_glClipPlanef
+#define glClipPlanefIMG wrap_glClipPlanefIMG
+#define glClipPlanefOES wrap_glClipPlanefOES
+#define glClipPlanex wrap_glClipPlanex
+#define glClipPlanexIMG wrap_glClipPlanexIMG
+#define glClipPlanexOES wrap_glClipPlanexOES
+#define glColor4f wrap_glColor4f
+#define glColor4ub wrap_glColor4ub
+#define glColor4x wrap_glColor4x
+#define glColor4xOES wrap_glColor4xOES
+#define glColorMask wrap_glColorMask
+#define glColorMaski wrap_glColorMaski
+#define glColorMaskiEXT wrap_glColorMaskiEXT
+#define glColorMaskiOES wrap_glColorMaskiOES
+#define glColorPointer wrap_glColorPointer
+#define glCompileShader wrap_glCompileShader
+#define glCompressedTexImage2D wrap_glCompressedTexImage2D
+#define glCompressedTexImage3D wrap_glCompressedTexImage3D
+#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES
+#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D
+#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D
+#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES
+#define glCopyBufferSubData wrap_glCopyBufferSubData
+#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV
+#define glCopyImageSubData wrap_glCopyImageSubData
+#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT
+#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES
+#define glCopyPathNV wrap_glCopyPathNV
+#define glCopyTexImage2D wrap_glCopyTexImage2D
+#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D
+#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D
+#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES
+#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE
+#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV
+#define glCoverFillPathNV wrap_glCoverFillPathNV
+#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV
+#define glCoverStrokePathNV wrap_glCoverStrokePathNV
+#define glCoverageMaskNV wrap_glCoverageMaskNV
+#define glCoverageModulationNV wrap_glCoverageModulationNV
+#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV
+#define glCoverageOperationNV wrap_glCoverageOperationNV
+#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL
+#define glCreateProgram wrap_glCreateProgram
+#define glCreateShader wrap_glCreateShader
+#define glCreateShaderProgramv wrap_glCreateShaderProgramv
+#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT
+#define glCullFace wrap_glCullFace
+#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES
+#define glDebugMessageCallback wrap_glDebugMessageCallback
+#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR
+#define glDebugMessageControl wrap_glDebugMessageControl
+#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR
+#define glDebugMessageInsert wrap_glDebugMessageInsert
+#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR
+#define glDeleteBuffers wrap_glDeleteBuffers
+#define glDeleteFencesNV wrap_glDeleteFencesNV
+#define glDeleteFramebuffers wrap_glDeleteFramebuffers
+#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES
+#define glDeletePathsNV wrap_glDeletePathsNV
+#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD
+#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL
+#define glDeleteProgram wrap_glDeleteProgram
+#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines
+#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT
+#define glDeleteQueries wrap_glDeleteQueries
+#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT
+#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers
+#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES
+#define glDeleteSamplers wrap_glDeleteSamplers
+#define glDeleteShader wrap_glDeleteShader
+#define glDeleteSync wrap_glDeleteSync
+#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE
+#define glDeleteTextures wrap_glDeleteTextures
+#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks
+#define glDeleteVertexArrays wrap_glDeleteVertexArrays
+#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES
+#define glDepthFunc wrap_glDepthFunc
+#define glDepthMask wrap_glDepthMask
+#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV
+#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV
+#define glDepthRangef wrap_glDepthRangef
+#define glDepthRangefOES wrap_glDepthRangefOES
+#define glDepthRangex wrap_glDepthRangex
+#define glDepthRangexOES wrap_glDepthRangexOES
+#define glDetachShader wrap_glDetachShader
+#define glDisable wrap_glDisable
+#define glDisableClientState wrap_glDisableClientState
+#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM
+#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray
+#define glDisablei wrap_glDisablei
+#define glDisableiEXT wrap_glDisableiEXT
+#define glDisableiNV wrap_glDisableiNV
+#define glDisableiOES wrap_glDisableiOES
+#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT
+#define glDispatchCompute wrap_glDispatchCompute
+#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect
+#define glDrawArrays wrap_glDrawArrays
+#define glDrawArraysIndirect wrap_glDrawArraysIndirect
+#define glDrawArraysInstanced wrap_glDrawArraysInstanced
+#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE
+#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT
+#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT
+#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV
+#define glDrawBuffers wrap_glDrawBuffers
+#define glDrawBuffersEXT wrap_glDrawBuffersEXT
+#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT
+#define glDrawBuffersNV wrap_glDrawBuffersNV
+#define glDrawElements wrap_glDrawElements
+#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex
+#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT
+#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES
+#define glDrawElementsIndirect wrap_glDrawElementsIndirect
+#define glDrawElementsInstanced wrap_glDrawElementsInstanced
+#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE
+#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT
+#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex
+#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT
+#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT
+#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES
+#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT
+#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV
+#define glDrawRangeElements wrap_glDrawRangeElements
+#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex
+#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT
+#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES
+#define glDrawTexfOES wrap_glDrawTexfOES
+#define glDrawTexfvOES wrap_glDrawTexfvOES
+#define glDrawTexiOES wrap_glDrawTexiOES
+#define glDrawTexivOES wrap_glDrawTexivOES
+#define glDrawTexsOES wrap_glDrawTexsOES
+#define glDrawTexsvOES wrap_glDrawTexsvOES
+#define glDrawTexxOES wrap_glDrawTexxOES
+#define glDrawTexxvOES wrap_glDrawTexxvOES
+#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES
+#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES
+#define glEnable wrap_glEnable
+#define glEnableClientState wrap_glEnableClientState
+#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM
+#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray
+#define glEnablei wrap_glEnablei
+#define glEnableiEXT wrap_glEnableiEXT
+#define glEnableiNV wrap_glEnableiNV
+#define glEnableiOES wrap_glEnableiOES
+#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV
+#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD
+#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL
+#define glEndQuery wrap_glEndQuery
+#define glEndQueryEXT wrap_glEndQueryEXT
+#define glEndTilingQCOM wrap_glEndTilingQCOM
+#define glEndTransformFeedback wrap_glEndTransformFeedback
+#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM
+#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM
+#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM
+#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM
+#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM
+#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM
+#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM
+#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM
+#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM
+#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM
+#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM
+#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM
+#define glFenceSync wrap_glFenceSync
+#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE
+#define glFinish wrap_glFinish
+#define glFinishFenceNV wrap_glFinishFenceNV
+#define glFlush wrap_glFlush
+#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange
+#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT
+#define glFogf wrap_glFogf
+#define glFogfv wrap_glFogfv
+#define glFogx wrap_glFogx
+#define glFogxOES wrap_glFogxOES
+#define glFogxv wrap_glFogxv
+#define glFogxvOES wrap_glFogxvOES
+#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV
+#define glFramebufferParameteri wrap_glFramebufferParameteri
+#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer
+#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES
+#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV
+#define glFramebufferTexture wrap_glFramebufferTexture
+#define glFramebufferTexture2D wrap_glFramebufferTexture2D
+#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT
+#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG
+#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES
+#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES
+#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT
+#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer
+#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR
+#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR
+#define glFramebufferTextureOES wrap_glFramebufferTextureOES
+#define glFrontFace wrap_glFrontFace
+#define glFrustumf wrap_glFrustumf
+#define glFrustumfOES wrap_glFrustumfOES
+#define glFrustumx wrap_glFrustumx
+#define glFrustumxOES wrap_glFrustumxOES
+#define glGenBuffers wrap_glGenBuffers
+#define glGenFencesNV wrap_glGenFencesNV
+#define glGenFramebuffers wrap_glGenFramebuffers
+#define glGenFramebuffersOES wrap_glGenFramebuffersOES
+#define glGenPathsNV wrap_glGenPathsNV
+#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD
+#define glGenProgramPipelines wrap_glGenProgramPipelines
+#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT
+#define glGenQueries wrap_glGenQueries
+#define glGenQueriesEXT wrap_glGenQueriesEXT
+#define glGenRenderbuffers wrap_glGenRenderbuffers
+#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES
+#define glGenSamplers wrap_glGenSamplers
+#define glGenTextures wrap_glGenTextures
+#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks
+#define glGenVertexArrays wrap_glGenVertexArrays
+#define glGenVertexArraysOES wrap_glGenVertexArraysOES
+#define glGenerateMipmap wrap_glGenerateMipmap
+#define glGenerateMipmapOES wrap_glGenerateMipmapOES
+#define glGetActiveAttrib wrap_glGetActiveAttrib
+#define glGetActiveUniform wrap_glGetActiveUniform
+#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName
+#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv
+#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv
+#define glGetAttachedShaders wrap_glGetAttachedShaders
+#define glGetAttribLocation wrap_glGetAttribLocation
+#define glGetBooleani_v wrap_glGetBooleani_v
+#define glGetBooleanv wrap_glGetBooleanv
+#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v
+#define glGetBufferParameteriv wrap_glGetBufferParameteriv
+#define glGetBufferPointerv wrap_glGetBufferPointerv
+#define glGetBufferPointervOES wrap_glGetBufferPointervOES
+#define glGetClipPlanef wrap_glGetClipPlanef
+#define glGetClipPlanefOES wrap_glGetClipPlanefOES
+#define glGetClipPlanex wrap_glGetClipPlanex
+#define glGetClipPlanexOES wrap_glGetClipPlanexOES
+#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV
+#define glGetDebugMessageLog wrap_glGetDebugMessageLog
+#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR
+#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM
+#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM
+#define glGetError wrap_glGetError
+#define glGetFenceivNV wrap_glGetFenceivNV
+#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL
+#define glGetFixedv wrap_glGetFixedv
+#define glGetFixedvOES wrap_glGetFixedvOES
+#define glGetFloati_vNV wrap_glGetFloati_vNV
+#define glGetFloatv wrap_glGetFloatv
+#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT
+#define glGetFragDataLocation wrap_glGetFragDataLocation
+#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv
+#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES
+#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv
+#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus
+#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT
+#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR
+#define glGetImageHandleNV wrap_glGetImageHandleNV
+#define glGetInteger64i_v wrap_glGetInteger64i_v
+#define glGetInteger64v wrap_glGetInteger64v
+#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE
+#define glGetIntegeri_v wrap_glGetIntegeri_v
+#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT
+#define glGetIntegerv wrap_glGetIntegerv
+#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV
+#define glGetInternalformativ wrap_glGetInternalformativ
+#define glGetLightfv wrap_glGetLightfv
+#define glGetLightxv wrap_glGetLightxv
+#define glGetLightxvOES wrap_glGetLightxvOES
+#define glGetMaterialfv wrap_glGetMaterialfv
+#define glGetMaterialxv wrap_glGetMaterialxv
+#define glGetMaterialxvOES wrap_glGetMaterialxvOES
+#define glGetMultisamplefv wrap_glGetMultisamplefv
+#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL
+#define glGetObjectLabel wrap_glGetObjectLabel
+#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT
+#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR
+#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel
+#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR
+#define glGetPathCommandsNV wrap_glGetPathCommandsNV
+#define glGetPathCoordsNV wrap_glGetPathCoordsNV
+#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV
+#define glGetPathLengthNV wrap_glGetPathLengthNV
+#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV
+#define glGetPathMetricsNV wrap_glGetPathMetricsNV
+#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV
+#define glGetPathParameterivNV wrap_glGetPathParameterivNV
+#define glGetPathSpacingNV wrap_glGetPathSpacingNV
+#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL
+#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD
+#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD
+#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD
+#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD
+#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD
+#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD
+#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL
+#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL
+#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL
+#define glGetPointerv wrap_glGetPointerv
+#define glGetPointervKHR wrap_glGetPointervKHR
+#define glGetProgramBinary wrap_glGetProgramBinary
+#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES
+#define glGetProgramInfoLog wrap_glGetProgramInfoLog
+#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv
+#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog
+#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT
+#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv
+#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT
+#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex
+#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation
+#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT
+#define glGetProgramResourceName wrap_glGetProgramResourceName
+#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV
+#define glGetProgramResourceiv wrap_glGetProgramResourceiv
+#define glGetProgramiv wrap_glGetProgramiv
+#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT
+#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT
+#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT
+#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv
+#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT
+#define glGetQueryiv wrap_glGetQueryiv
+#define glGetQueryivEXT wrap_glGetQueryivEXT
+#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv
+#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES
+#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv
+#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT
+#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES
+#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv
+#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT
+#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES
+#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv
+#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv
+#define glGetShaderInfoLog wrap_glGetShaderInfoLog
+#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat
+#define glGetShaderSource wrap_glGetShaderSource
+#define glGetShaderiv wrap_glGetShaderiv
+#define glGetString wrap_glGetString
+#define glGetStringi wrap_glGetStringi
+#define glGetSynciv wrap_glGetSynciv
+#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE
+#define glGetTexEnvfv wrap_glGetTexEnvfv
+#define glGetTexEnviv wrap_glGetTexEnviv
+#define glGetTexEnvxv wrap_glGetTexEnvxv
+#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES
+#define glGetTexGenfvOES wrap_glGetTexGenfvOES
+#define glGetTexGenivOES wrap_glGetTexGenivOES
+#define glGetTexGenxvOES wrap_glGetTexGenxvOES
+#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv
+#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv
+#define glGetTexParameterIiv wrap_glGetTexParameterIiv
+#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT
+#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES
+#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv
+#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT
+#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES
+#define glGetTexParameterfv wrap_glGetTexParameterfv
+#define glGetTexParameteriv wrap_glGetTexParameteriv
+#define glGetTexParameterxv wrap_glGetTexParameterxv
+#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES
+#define glGetTextureHandleNV wrap_glGetTextureHandleNV
+#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV
+#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying
+#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE
+#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex
+#define glGetUniformIndices wrap_glGetUniformIndices
+#define glGetUniformLocation wrap_glGetUniformLocation
+#define glGetUniformfv wrap_glGetUniformfv
+#define glGetUniformiv wrap_glGetUniformiv
+#define glGetUniformuiv wrap_glGetUniformuiv
+#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv
+#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv
+#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv
+#define glGetVertexAttribfv wrap_glGetVertexAttribfv
+#define glGetVertexAttribiv wrap_glGetVertexAttribiv
+#define glGetnUniformfv wrap_glGetnUniformfv
+#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT
+#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR
+#define glGetnUniformiv wrap_glGetnUniformiv
+#define glGetnUniformivEXT wrap_glGetnUniformivEXT
+#define glGetnUniformivKHR wrap_glGetnUniformivKHR
+#define glGetnUniformuiv wrap_glGetnUniformuiv
+#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR
+#define glHint wrap_glHint
+#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT
+#define glInterpolatePathsNV wrap_glInterpolatePathsNV
+#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer
+#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer
+#define glIsBuffer wrap_glIsBuffer
+#define glIsEnabled wrap_glIsEnabled
+#define glIsEnabledi wrap_glIsEnabledi
+#define glIsEnablediEXT wrap_glIsEnablediEXT
+#define glIsEnablediNV wrap_glIsEnablediNV
+#define glIsEnablediOES wrap_glIsEnablediOES
+#define glIsFenceNV wrap_glIsFenceNV
+#define glIsFramebuffer wrap_glIsFramebuffer
+#define glIsFramebufferOES wrap_glIsFramebufferOES
+#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV
+#define glIsPathNV wrap_glIsPathNV
+#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV
+#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV
+#define glIsProgram wrap_glIsProgram
+#define glIsProgramPipeline wrap_glIsProgramPipeline
+#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT
+#define glIsQuery wrap_glIsQuery
+#define glIsQueryEXT wrap_glIsQueryEXT
+#define glIsRenderbuffer wrap_glIsRenderbuffer
+#define glIsRenderbufferOES wrap_glIsRenderbufferOES
+#define glIsSampler wrap_glIsSampler
+#define glIsShader wrap_glIsShader
+#define glIsSync wrap_glIsSync
+#define glIsSyncAPPLE wrap_glIsSyncAPPLE
+#define glIsTexture wrap_glIsTexture
+#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV
+#define glIsTransformFeedback wrap_glIsTransformFeedback
+#define glIsVertexArray wrap_glIsVertexArray
+#define glIsVertexArrayOES wrap_glIsVertexArrayOES
+#define glLabelObjectEXT wrap_glLabelObjectEXT
+#define glLightModelf wrap_glLightModelf
+#define glLightModelfv wrap_glLightModelfv
+#define glLightModelx wrap_glLightModelx
+#define glLightModelxOES wrap_glLightModelxOES
+#define glLightModelxv wrap_glLightModelxv
+#define glLightModelxvOES wrap_glLightModelxvOES
+#define glLightf wrap_glLightf
+#define glLightfv wrap_glLightfv
+#define glLightx wrap_glLightx
+#define glLightxOES wrap_glLightxOES
+#define glLightxv wrap_glLightxv
+#define glLightxvOES wrap_glLightxvOES
+#define glLineWidth wrap_glLineWidth
+#define glLineWidthx wrap_glLineWidthx
+#define glLineWidthxOES wrap_glLineWidthxOES
+#define glLinkProgram wrap_glLinkProgram
+#define glLoadIdentity wrap_glLoadIdentity
+#define glLoadMatrixf wrap_glLoadMatrixf
+#define glLoadMatrixx wrap_glLoadMatrixx
+#define glLoadMatrixxOES wrap_glLoadMatrixxOES
+#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES
+#define glLogicOp wrap_glLogicOp
+#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV
+#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV
+#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV
+#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV
+#define glMapBufferOES wrap_glMapBufferOES
+#define glMapBufferRange wrap_glMapBufferRange
+#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT
+#define glMaterialf wrap_glMaterialf
+#define glMaterialfv wrap_glMaterialfv
+#define glMaterialx wrap_glMaterialx
+#define glMaterialxOES wrap_glMaterialxOES
+#define glMaterialxv wrap_glMaterialxv
+#define glMaterialxvOES wrap_glMaterialxvOES
+#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES
+#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV
+#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV
+#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV
+#define glMatrixMode wrap_glMatrixMode
+#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV
+#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV
+#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV
+#define glMemoryBarrier wrap_glMemoryBarrier
+#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion
+#define glMinSampleShading wrap_glMinSampleShading
+#define glMinSampleShadingOES wrap_glMinSampleShadingOES
+#define glMultMatrixf wrap_glMultMatrixf
+#define glMultMatrixx wrap_glMultMatrixx
+#define glMultMatrixxOES wrap_glMultMatrixxOES
+#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT
+#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT
+#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT
+#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES
+#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT
+#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT
+#define glMultiTexCoord4f wrap_glMultiTexCoord4f
+#define glMultiTexCoord4x wrap_glMultiTexCoord4x
+#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES
+#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV
+#define glNormal3f wrap_glNormal3f
+#define glNormal3x wrap_glNormal3x
+#define glNormal3xOES wrap_glNormal3xOES
+#define glNormalPointer wrap_glNormalPointer
+#define glObjectLabel wrap_glObjectLabel
+#define glObjectLabelKHR wrap_glObjectLabelKHR
+#define glObjectPtrLabel wrap_glObjectPtrLabel
+#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR
+#define glOrthof wrap_glOrthof
+#define glOrthofOES wrap_glOrthofOES
+#define glOrthox wrap_glOrthox
+#define glOrthoxOES wrap_glOrthoxOES
+#define glPatchParameteri wrap_glPatchParameteri
+#define glPatchParameteriEXT wrap_glPatchParameteriEXT
+#define glPatchParameteriOES wrap_glPatchParameteriOES
+#define glPathCommandsNV wrap_glPathCommandsNV
+#define glPathCoordsNV wrap_glPathCoordsNV
+#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV
+#define glPathDashArrayNV wrap_glPathDashArrayNV
+#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV
+#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV
+#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV
+#define glPathGlyphsNV wrap_glPathGlyphsNV
+#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV
+#define glPathParameterfNV wrap_glPathParameterfNV
+#define glPathParameterfvNV wrap_glPathParameterfvNV
+#define glPathParameteriNV wrap_glPathParameteriNV
+#define glPathParameterivNV wrap_glPathParameterivNV
+#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV
+#define glPathStencilFuncNV wrap_glPathStencilFuncNV
+#define glPathStringNV wrap_glPathStringNV
+#define glPathSubCommandsNV wrap_glPathSubCommandsNV
+#define glPathSubCoordsNV wrap_glPathSubCoordsNV
+#define glPauseTransformFeedback wrap_glPauseTransformFeedback
+#define glPixelStorei wrap_glPixelStorei
+#define glPointAlongPathNV wrap_glPointAlongPathNV
+#define glPointParameterf wrap_glPointParameterf
+#define glPointParameterfv wrap_glPointParameterfv
+#define glPointParameterx wrap_glPointParameterx
+#define glPointParameterxOES wrap_glPointParameterxOES
+#define glPointParameterxv wrap_glPointParameterxv
+#define glPointParameterxvOES wrap_glPointParameterxvOES
+#define glPointSize wrap_glPointSize
+#define glPointSizePointerOES wrap_glPointSizePointerOES
+#define glPointSizex wrap_glPointSizex
+#define glPointSizexOES wrap_glPointSizexOES
+#define glPolygonModeNV wrap_glPolygonModeNV
+#define glPolygonOffset wrap_glPolygonOffset
+#define glPolygonOffsetx wrap_glPolygonOffsetx
+#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES
+#define glPopDebugGroup wrap_glPopDebugGroup
+#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR
+#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT
+#define glPopMatrix wrap_glPopMatrix
+#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox
+#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT
+#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES
+#define glProgramBinary wrap_glProgramBinary
+#define glProgramBinaryOES wrap_glProgramBinaryOES
+#define glProgramParameteri wrap_glProgramParameteri
+#define glProgramParameteriEXT wrap_glProgramParameteriEXT
+#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV
+#define glProgramUniform1f wrap_glProgramUniform1f
+#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT
+#define glProgramUniform1fv wrap_glProgramUniform1fv
+#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT
+#define glProgramUniform1i wrap_glProgramUniform1i
+#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT
+#define glProgramUniform1iv wrap_glProgramUniform1iv
+#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT
+#define glProgramUniform1ui wrap_glProgramUniform1ui
+#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT
+#define glProgramUniform1uiv wrap_glProgramUniform1uiv
+#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT
+#define glProgramUniform2f wrap_glProgramUniform2f
+#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT
+#define glProgramUniform2fv wrap_glProgramUniform2fv
+#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT
+#define glProgramUniform2i wrap_glProgramUniform2i
+#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT
+#define glProgramUniform2iv wrap_glProgramUniform2iv
+#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT
+#define glProgramUniform2ui wrap_glProgramUniform2ui
+#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT
+#define glProgramUniform2uiv wrap_glProgramUniform2uiv
+#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT
+#define glProgramUniform3f wrap_glProgramUniform3f
+#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT
+#define glProgramUniform3fv wrap_glProgramUniform3fv
+#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT
+#define glProgramUniform3i wrap_glProgramUniform3i
+#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT
+#define glProgramUniform3iv wrap_glProgramUniform3iv
+#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT
+#define glProgramUniform3ui wrap_glProgramUniform3ui
+#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT
+#define glProgramUniform3uiv wrap_glProgramUniform3uiv
+#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT
+#define glProgramUniform4f wrap_glProgramUniform4f
+#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT
+#define glProgramUniform4fv wrap_glProgramUniform4fv
+#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT
+#define glProgramUniform4i wrap_glProgramUniform4i
+#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT
+#define glProgramUniform4iv wrap_glProgramUniform4iv
+#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT
+#define glProgramUniform4ui wrap_glProgramUniform4ui
+#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT
+#define glProgramUniform4uiv wrap_glProgramUniform4uiv
+#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT
+#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV
+#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV
+#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv
+#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT
+#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv
+#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT
+#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv
+#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT
+#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv
+#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT
+#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv
+#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT
+#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv
+#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT
+#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv
+#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT
+#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv
+#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT
+#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv
+#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT
+#define glPushDebugGroup wrap_glPushDebugGroup
+#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR
+#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT
+#define glPushMatrix wrap_glPushMatrix
+#define glQueryCounterEXT wrap_glQueryCounterEXT
+#define glQueryMatrixxOES wrap_glQueryMatrixxOES
+#define glRasterSamplesEXT wrap_glRasterSamplesEXT
+#define glReadBuffer wrap_glReadBuffer
+#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT
+#define glReadBufferNV wrap_glReadBufferNV
+#define glReadPixels wrap_glReadPixels
+#define glReadnPixels wrap_glReadnPixels
+#define glReadnPixelsEXT wrap_glReadnPixelsEXT
+#define glReadnPixelsKHR wrap_glReadnPixelsKHR
+#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler
+#define glRenderbufferStorage wrap_glRenderbufferStorage
+#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample
+#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE
+#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE
+#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT
+#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG
+#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV
+#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES
+#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV
+#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE
+#define glResumeTransformFeedback wrap_glResumeTransformFeedback
+#define glRotatef wrap_glRotatef
+#define glRotatex wrap_glRotatex
+#define glRotatexOES wrap_glRotatexOES
+#define glSampleCoverage wrap_glSampleCoverage
+#define glSampleCoveragex wrap_glSampleCoveragex
+#define glSampleCoveragexOES wrap_glSampleCoveragexOES
+#define glSampleMaski wrap_glSampleMaski
+#define glSamplerParameterIiv wrap_glSamplerParameterIiv
+#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT
+#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES
+#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv
+#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT
+#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES
+#define glSamplerParameterf wrap_glSamplerParameterf
+#define glSamplerParameterfv wrap_glSamplerParameterfv
+#define glSamplerParameteri wrap_glSamplerParameteri
+#define glSamplerParameteriv wrap_glSamplerParameteriv
+#define glScalef wrap_glScalef
+#define glScalex wrap_glScalex
+#define glScalexOES wrap_glScalexOES
+#define glScissor wrap_glScissor
+#define glScissorArrayvNV wrap_glScissorArrayvNV
+#define glScissorIndexedNV wrap_glScissorIndexedNV
+#define glScissorIndexedvNV wrap_glScissorIndexedvNV
+#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD
+#define glSetFenceNV wrap_glSetFenceNV
+#define glShadeModel wrap_glShadeModel
+#define glShaderBinary wrap_glShaderBinary
+#define glShaderSource wrap_glShaderSource
+#define glStartTilingQCOM wrap_glStartTilingQCOM
+#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV
+#define glStencilFillPathNV wrap_glStencilFillPathNV
+#define glStencilFunc wrap_glStencilFunc
+#define glStencilFuncSeparate wrap_glStencilFuncSeparate
+#define glStencilMask wrap_glStencilMask
+#define glStencilMaskSeparate wrap_glStencilMaskSeparate
+#define glStencilOp wrap_glStencilOp
+#define glStencilOpSeparate wrap_glStencilOpSeparate
+#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV
+#define glStencilStrokePathNV wrap_glStencilStrokePathNV
+#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV
+#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV
+#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV
+#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV
+#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV
+#define glTestFenceNV wrap_glTestFenceNV
+#define glTexBuffer wrap_glTexBuffer
+#define glTexBufferEXT wrap_glTexBufferEXT
+#define glTexBufferOES wrap_glTexBufferOES
+#define glTexBufferRange wrap_glTexBufferRange
+#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT
+#define glTexBufferRangeOES wrap_glTexBufferRangeOES
+#define glTexCoordPointer wrap_glTexCoordPointer
+#define glTexEnvf wrap_glTexEnvf
+#define glTexEnvfv wrap_glTexEnvfv
+#define glTexEnvi wrap_glTexEnvi
+#define glTexEnviv wrap_glTexEnviv
+#define glTexEnvx wrap_glTexEnvx
+#define glTexEnvxOES wrap_glTexEnvxOES
+#define glTexEnvxv wrap_glTexEnvxv
+#define glTexEnvxvOES wrap_glTexEnvxvOES
+#define glTexGenfOES wrap_glTexGenfOES
+#define glTexGenfvOES wrap_glTexGenfvOES
+#define glTexGeniOES wrap_glTexGeniOES
+#define glTexGenivOES wrap_glTexGenivOES
+#define glTexGenxOES wrap_glTexGenxOES
+#define glTexGenxvOES wrap_glTexGenxvOES
+#define glTexImage2D wrap_glTexImage2D
+#define glTexImage3D wrap_glTexImage3D
+#define glTexImage3DOES wrap_glTexImage3DOES
+#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT
+#define glTexParameterIiv wrap_glTexParameterIiv
+#define glTexParameterIivEXT wrap_glTexParameterIivEXT
+#define glTexParameterIivOES wrap_glTexParameterIivOES
+#define glTexParameterIuiv wrap_glTexParameterIuiv
+#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT
+#define glTexParameterIuivOES wrap_glTexParameterIuivOES
+#define glTexParameterf wrap_glTexParameterf
+#define glTexParameterfv wrap_glTexParameterfv
+#define glTexParameteri wrap_glTexParameteri
+#define glTexParameteriv wrap_glTexParameteriv
+#define glTexParameterx wrap_glTexParameterx
+#define glTexParameterxOES wrap_glTexParameterxOES
+#define glTexParameterxv wrap_glTexParameterxv
+#define glTexParameterxvOES wrap_glTexParameterxvOES
+#define glTexStorage1DEXT wrap_glTexStorage1DEXT
+#define glTexStorage2D wrap_glTexStorage2D
+#define glTexStorage2DEXT wrap_glTexStorage2DEXT
+#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample
+#define glTexStorage3D wrap_glTexStorage3D
+#define glTexStorage3DEXT wrap_glTexStorage3DEXT
+#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample
+#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES
+#define glTexSubImage2D wrap_glTexSubImage2D
+#define glTexSubImage3D wrap_glTexSubImage3D
+#define glTexSubImage3DOES wrap_glTexSubImage3DOES
+#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT
+#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT
+#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT
+#define glTextureViewEXT wrap_glTextureViewEXT
+#define glTextureViewOES wrap_glTextureViewOES
+#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings
+#define glTransformPathNV wrap_glTransformPathNV
+#define glTranslatef wrap_glTranslatef
+#define glTranslatex wrap_glTranslatex
+#define glTranslatexOES wrap_glTranslatexOES
+#define glUniform1f wrap_glUniform1f
+#define glUniform1fv wrap_glUniform1fv
+#define glUniform1i wrap_glUniform1i
+#define glUniform1iv wrap_glUniform1iv
+#define glUniform1ui wrap_glUniform1ui
+#define glUniform1uiv wrap_glUniform1uiv
+#define glUniform2f wrap_glUniform2f
+#define glUniform2fv wrap_glUniform2fv
+#define glUniform2i wrap_glUniform2i
+#define glUniform2iv wrap_glUniform2iv
+#define glUniform2ui wrap_glUniform2ui
+#define glUniform2uiv wrap_glUniform2uiv
+#define glUniform3f wrap_glUniform3f
+#define glUniform3fv wrap_glUniform3fv
+#define glUniform3i wrap_glUniform3i
+#define glUniform3iv wrap_glUniform3iv
+#define glUniform3ui wrap_glUniform3ui
+#define glUniform3uiv wrap_glUniform3uiv
+#define glUniform4f wrap_glUniform4f
+#define glUniform4fv wrap_glUniform4fv
+#define glUniform4i wrap_glUniform4i
+#define glUniform4iv wrap_glUniform4iv
+#define glUniform4ui wrap_glUniform4ui
+#define glUniform4uiv wrap_glUniform4uiv
+#define glUniformBlockBinding wrap_glUniformBlockBinding
+#define glUniformHandleui64NV wrap_glUniformHandleui64NV
+#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV
+#define glUniformMatrix2fv wrap_glUniformMatrix2fv
+#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv
+#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV
+#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv
+#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV
+#define glUniformMatrix3fv wrap_glUniformMatrix3fv
+#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv
+#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV
+#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv
+#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV
+#define glUniformMatrix4fv wrap_glUniformMatrix4fv
+#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv
+#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV
+#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv
+#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV
+#define glUnmapBuffer wrap_glUnmapBuffer
+#define glUnmapBufferOES wrap_glUnmapBufferOES
+#define glUseProgram wrap_glUseProgram
+#define glUseProgramStages wrap_glUseProgramStages
+#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT
+#define glValidateProgram wrap_glValidateProgram
+#define glValidateProgramPipeline wrap_glValidateProgramPipeline
+#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT
+#define glVertexAttrib1f wrap_glVertexAttrib1f
+#define glVertexAttrib1fv wrap_glVertexAttrib1fv
+#define glVertexAttrib2f wrap_glVertexAttrib2f
+#define glVertexAttrib2fv wrap_glVertexAttrib2fv
+#define glVertexAttrib3f wrap_glVertexAttrib3f
+#define glVertexAttrib3fv wrap_glVertexAttrib3fv
+#define glVertexAttrib4f wrap_glVertexAttrib4f
+#define glVertexAttrib4fv wrap_glVertexAttrib4fv
+#define glVertexAttribBinding wrap_glVertexAttribBinding
+#define glVertexAttribDivisor wrap_glVertexAttribDivisor
+#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE
+#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT
+#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV
+#define glVertexAttribFormat wrap_glVertexAttribFormat
+#define glVertexAttribI4i wrap_glVertexAttribI4i
+#define glVertexAttribI4iv wrap_glVertexAttribI4iv
+#define glVertexAttribI4ui wrap_glVertexAttribI4ui
+#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv
+#define glVertexAttribIFormat wrap_glVertexAttribIFormat
+#define glVertexAttribIPointer wrap_glVertexAttribIPointer
+#define glVertexAttribPointer wrap_glVertexAttribPointer
+#define glVertexBindingDivisor wrap_glVertexBindingDivisor
+#define glVertexPointer wrap_glVertexPointer
+#define glViewport wrap_glViewport
+#define glViewportArrayvNV wrap_glViewportArrayvNV
+#define glViewportIndexedfNV wrap_glViewportIndexedfNV
+#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV
+#define glWaitSync wrap_glWaitSync
+#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE
+#define glWeightPathsNV wrap_glWeightPathsNV
+#define glWeightPointerOES wrap_glWeightPointerOES
+
+#endif // HWUI_GLES_WRAP_ENABLED
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 75dcf16..81363d9 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -241,7 +241,7 @@
     const Glop::Mesh::Indices& indices = mesh.indices;
     const Glop::Fill& fill = glop.fill;
 
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
 
     // ---------------------------------------------
     // ---------- Program + uniform setup ----------
@@ -286,7 +286,7 @@
                 roundedOutRadius);
     }
 
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
 
     // --------------------------------
     // ---------- Mesh setup ----------
@@ -339,7 +339,7 @@
     // Shader uniforms
     SkiaShader::apply(*mCaches, fill.skiaShaderData);
 
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
     Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) ?
             fill.skiaShaderData.bitmapData.bitmapTexture : nullptr;
     const AutoTexture autoCleanup(texture);
@@ -349,7 +349,7 @@
     // ------------------------------------
     blend().setFactors(glop.blend.src, glop.blend.dst);
 
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
 
     // ------------------------------------
     // ---------- Actual drawing ----------
@@ -379,7 +379,7 @@
         glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount);
     }
 
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
 
     // -----------------------------------
     // ---------- Mesh teardown ----------
@@ -391,7 +391,7 @@
         glDisableVertexAttribArray(colorLocation);
     }
 
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
 }
 
 void RenderState::dump() {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index cdd2da0..e7cf3ec 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -19,6 +19,7 @@
 
 #include "AnimationContext.h"
 #include "Caches.h"
+#include "Canvas.h"
 #include "DeferredLayerUpdater.h"
 #include "EglManager.h"
 #include "LayerUpdateQueue.h"
@@ -214,13 +215,13 @@
         // node(s) are non client / filler nodes.
         info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
         node->prepareTree(info);
-        GL_CHECKPOINT();
+        GL_CHECKPOINT(MODERATE);
     }
     mAnimationContext->runRemainingAnimations(info);
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
 
     freePrefetechedLayers();
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(MODERATE);
 
     if (CC_UNLIKELY(!mNativeWindow.get())) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -344,7 +345,7 @@
 
 #if HWUI_NEW_OPS
     FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
-            mRenderNodes, mLightCenter);
+            mRenderNodes, mLightCenter, mContentDrawBounds);
     mLayerUpdateQueue.clear();
     BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
             mOpaque, mLightInfo);
@@ -394,7 +395,7 @@
             backdropBounds.doIntersect(targetBounds);
             // Check if we have to draw something on the left side ...
             if (targetBounds.left < contentBounds.left) {
-                mCanvas->save(SkCanvas::kClip_SaveFlag);
+                mCanvas->save(SaveFlags::Clip);
                 if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
                                       contentBounds.left, targetBounds.bottom,
                                       SkRegion::kIntersect_Op)) {
@@ -407,7 +408,7 @@
             // ... or on the right side ...
             if (targetBounds.right > contentBounds.right &&
                 !targetBounds.isEmpty()) {
-                mCanvas->save(SkCanvas::kClip_SaveFlag);
+                mCanvas->save(SaveFlags::Clip);
                 if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
                                       targetBounds.right, targetBounds.bottom,
                                       SkRegion::kIntersect_Op)) {
@@ -420,7 +421,7 @@
             // ... or at the top ...
             if (targetBounds.top < contentBounds.top &&
                 !targetBounds.isEmpty()) {
-                mCanvas->save(SkCanvas::kClip_SaveFlag);
+                mCanvas->save(SaveFlags::Clip);
                 if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
                                       contentBounds.top,
                                       SkRegion::kIntersect_Op)) {
@@ -433,7 +434,7 @@
             // ... or at the bottom.
             if (targetBounds.bottom > contentBounds.bottom &&
                 !targetBounds.isEmpty()) {
-                mCanvas->save(SkCanvas::kClip_SaveFlag);
+                mCanvas->save(SaveFlags::Clip);
                 if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
                                       targetBounds.bottom, SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
@@ -442,7 +443,7 @@
             }
         } else if (layer == 1) { // Content
             // It gets cropped against the bounds of the backdrop to stay inside.
-            mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+            mCanvas->save(SaveFlags::MatrixClip);
 
             // We shift and clip the content to match its final location in the window.
             const float left = mContentDrawBounds.left;
@@ -468,7 +469,7 @@
     bool drew = mCanvas->finish();
 #endif
 
-    GL_CHECKPOINT();
+    GL_CHECKPOINT(LOW);
 
     // Even if we decided to cancel the frame, from the perspective of jank
     // metrics the frame was swapped at this point
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index db6402c..a5fd712 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -32,7 +32,7 @@
         canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
         card = TestUtils::createNode(0, 0, 200, 400,
                 [](RenderProperties& props, TestCanvas& canvas) {
-            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            canvas.save(SaveFlags::MatrixClip);
             {
                 canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
                 canvas.translate(100, 100);
@@ -43,7 +43,7 @@
             }
             canvas.restore();
 
-            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            canvas.save(SaveFlags::MatrixClip);
             {
                 SkPath clipCircle;
                 clipCircle.addCircle(100, 300, 100);
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index c899850..6904bec 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -34,18 +34,18 @@
         card = TestUtils::createNode(0, 0, 400, 800,
                 [](RenderProperties& props, TestCanvas& canvas) {
             // nested clipped saveLayers
-            canvas.saveLayerAlpha(0, 0, 400, 400, 200, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer);
             canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode);
             canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op);
-            canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
             canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
             canvas.restore();
             canvas.restore();
 
             // single unclipped saveLayer
-            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            canvas.save(SaveFlags::MatrixClip);
             canvas.translate(0, 400);
-            canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::SaveFlags(0)); // unclipped
+            canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped
             SkPaint paint;
             paint.setAntiAlias(true);
             paint.setColor(Color::Green_700);
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 0cba344..6d27c9d 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -69,7 +69,7 @@
             float cellSize = floorf(width / 7 - cellSpace);
 
             // each combination of strokeWidth + style gets a column
-            int outerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            int outerCount = canvas.save(SaveFlags::MatrixClip);
             SkPaint paint;
             paint.setAntiAlias(true);
             SkPaint::Style styles[] = {
@@ -79,9 +79,9 @@
                 for (auto strokeWidth : { 0.0f, 0.5f, 8.0f }) {
                     paint.setStrokeWidth(strokeWidth);
                     // fill column with each op
-                    int middleCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+                    int middleCount = canvas.save(SaveFlags::MatrixClip);
                     for (auto op : ops) {
-                        int innerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+                        int innerCount = canvas.save(SaveFlags::MatrixClip);
                         canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op);
                         canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
                         op(canvas, cellSize, paint);
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 2e59eb4..83af148 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -78,8 +78,8 @@
     StartBenchmarkTiming();
     for (int i = 0; i < iters; ++i) {
         canvas.reset(100, 100);
-        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
-        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
+        canvas.save(SaveFlags::MatrixClip);
         MicroBench::DoNotOptimize(&canvas);
         canvas.restore();
         canvas.restore();
@@ -121,12 +121,12 @@
     for (int i = 0; i < iters; ++i) {
         canvas.reset(100, 100);
         {
-            canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+            canvas.save(SaveFlags::MatrixClip);
             canvas.drawRect(0, 0, 100, 100, rectPaint);
             canvas.restore();
         }
         {
-            canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+            canvas.save(SaveFlags::MatrixClip);
             canvas.translate(10, 10);
             canvas.drawBitmap(iconBitmap, 0, 0, nullptr);
             canvas.restore();
@@ -151,8 +151,8 @@
 
     StartBenchmarkTiming();
     for (int i = 0; i < iters; ++i) {
-        state.save(SkCanvas::kMatrixClip_SaveFlag);
-        state.save(SkCanvas::kMatrixClip_SaveFlag);
+        state.save(SaveFlags::MatrixClip);
+        state.save(SaveFlags::MatrixClip);
         MicroBench::DoNotOptimize(&state);
         state.restore();
         state.restore();
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 67c95e2..f9c2b67 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -47,7 +47,7 @@
 
         // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
         // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         for (int i = 0; i < 30; i++) {
             canvas.translate(0, 10);
             canvas.drawRect(0, 0, 10, 10, paint);
diff --git a/libs/hwui/tests/unit/BufferPoolTests.cpp b/libs/hwui/tests/unit/BufferPoolTests.cpp
index 09bd302..44e6d3a 100644
--- a/libs/hwui/tests/unit/BufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/BufferPoolTests.cpp
@@ -36,6 +36,7 @@
             ASSERT_EQ(bufferCount - i, pool->getAvailableBufferCount());
             acquiredBuffers[i] = pool->acquire();
             ASSERT_NE(nullptr, acquiredBuffers[i]);
+            ASSERT_TRUE(acquiredBuffers[i]->isUniqueRef());
         }
 
         for (size_t i = 0; i < bufferCount; i++) {
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index 4df2687..68d74ee 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -16,6 +16,7 @@
 
 #include "CanvasState.h"
 
+#include "Canvas.h"
 #include "Matrix.h"
 #include "Rect.h"
 #include "utils/LinearAllocator.h"
@@ -23,7 +24,6 @@
 #include <gtest/gtest.h>
 #include <SkPath.h>
 #include <SkRegion.h>
-#include <SkCanvas.h>
 
 namespace android {
 namespace uirenderer {
@@ -83,7 +83,7 @@
     state.initializeSaveStack(200, 200,
             0, 0, 200, 200, Vector3());
 
-    state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+    state.save(SaveFlags::MatrixClip);
     {
         // rotated clip causes complex clip
         state.rotate(10);
@@ -93,7 +93,7 @@
     }
     state.restore();
 
-    state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+    state.save(SaveFlags::MatrixClip);
     {
         // subtracted clip causes complex clip
         EXPECT_TRUE(state.clipIsSimple());
@@ -102,7 +102,7 @@
     }
     state.restore();
 
-    state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+    state.save(SaveFlags::MatrixClip);
     {
         // complex path causes complex clip
         SkPath path;
@@ -119,7 +119,7 @@
     state.initializeSaveStack(200, 200,
             0, 0, 200, 200, Vector3());
 
-    state.save(SkCanvas::kClip_SaveFlag);
+    state.save(SaveFlags::Clip);
     {
         state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
         ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
@@ -129,7 +129,7 @@
 
     Matrix4 simpleTranslate;
     simpleTranslate.loadTranslate(10, 10, 0);
-    state.save(SkCanvas::kMatrix_SaveFlag);
+    state.save(SaveFlags::Matrix);
     {
         state.translate(10, 10, 0);
         EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
@@ -143,7 +143,7 @@
     state.initializeSaveStack(200, 200,
             0, 0, 200, 200, Vector3());
 
-    state.save(SkCanvas::kMatrix_SaveFlag); // NOTE: clip not saved
+    state.save(SaveFlags::Matrix); // NOTE: clip not saved
     {
         state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
         ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
@@ -153,7 +153,7 @@
 
     Matrix4 simpleTranslate;
     simpleTranslate.loadTranslate(10, 10, 0);
-    state.save(SkCanvas::kClip_SaveFlag); // NOTE: matrix not saved
+    state.save(SaveFlags::Clip); // NOTE: matrix not saved
     {
         state.translate(10, 10, 0);
         EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index b51bd2f..618df14 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -167,7 +167,7 @@
 TEST(FrameBuilder, simpleRejection) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
@@ -198,7 +198,7 @@
 
         // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
         // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         for (int i = 0; i < LOOPS; i++) {
             canvas.translate(0, 10);
             canvas.drawRect(0, 0, 10, 10, SkPaint());
@@ -215,7 +215,8 @@
             << "Expect number of ops = 2 * loop count";
 }
 
-TEST(FrameBuilder, clippedMerging) {
+// TODO: Disabled due to b/26793764
+TEST(FrameBuilder, DISABLED_clippedMerging) {
     class ClippedMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -336,7 +337,7 @@
 
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
@@ -380,7 +381,7 @@
         paint.setColor(SK_ColorDKGRAY);
         canvas.drawRect(0, 0, 200, 200, paint);
 
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.translate(40, 40);
         canvas.drawRenderNode(child.get());
         canvas.restore();
@@ -448,7 +449,7 @@
 
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(10, 10, 190, 190, SkPaint());
         canvas.restore();
     });
@@ -512,10 +513,10 @@
 
     auto node = TestUtils::createNode(0, 0, 800, 800,
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
         {
             canvas.drawRect(0, 0, 800, 800, SkPaint());
-            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
             {
                 canvas.drawRect(0, 0, 400, 400, SkPaint());
             }
@@ -534,9 +535,9 @@
 TEST(FrameBuilder, saveLayer_contentRejection) {
         auto node = TestUtils::createNode(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
-        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
 
         // draw within save layer may still be recorded, but shouldn't be drawn
         canvas.drawRect(200, 200, 400, 400, SkPaint());
@@ -583,7 +584,7 @@
 
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0));
+        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
     });
@@ -632,12 +633,12 @@
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
 
-        int restoreTo = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        int restoreTo = canvas.save(SaveFlags::MatrixClip);
         canvas.scale(2, 2);
-        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SkCanvas::kMatrixClip_SaveFlag);
-        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SkCanvas::kMatrixClip_SaveFlag);
-        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SkCanvas::kMatrixClip_SaveFlag);
-        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SkCanvas::kMatrixClip_SaveFlag);
+        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
+        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
+        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
+        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
         canvas.drawRect(0, 0, 100, 100, SkPaint());
         canvas.restoreToCount(restoreTo);
     });
@@ -695,9 +696,9 @@
 
     auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SkCanvas::SaveFlags)0); // unclipped
-        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); // clipped
-        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SkCanvas::SaveFlags)0); // unclipped
+        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
+        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
+        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
         canvas.drawRect(200, 200, 300, 300, SkPaint());
         canvas.restore();
         canvas.restore();
@@ -849,7 +850,7 @@
         paint.setColor(SK_ColorDKGRAY);
         canvas.drawRect(0, 0, 200, 200, paint);
 
-        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
         canvas.drawRenderNode(childPtr);
         canvas.restore();
     });
@@ -987,7 +988,7 @@
     });
     auto parent = TestUtils::createNode(0, 0, 100, 100,
             [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
         canvas.drawRenderNode(receiverBackground.get());
         canvas.drawRenderNode(child.get());
@@ -1071,7 +1072,7 @@
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // save/restore outside of reorderBarrier, so they don't get moved out of place
         canvas.translate(20, 10);
-        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+        int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
         canvas.insertReorderBarrier(true);
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
         canvas.insertReorderBarrier(false);
@@ -1111,7 +1112,7 @@
             [](RenderProperties& props, RecordingCanvas& canvas) {
         props.mutateLayerProperties().setType(LayerType::RenderLayer);
         canvas.insertReorderBarrier(true);
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.translate(20, 10);
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
         canvas.restore();
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 41e44fc..4a635fb 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -32,7 +32,7 @@
 RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0));
+        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
     });
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index ff098c8..01bfc5a 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -35,7 +35,7 @@
 
 TEST(RecordingCanvas, emptyPlayback) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.restore();
     });
     playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
@@ -43,7 +43,7 @@
 
 TEST(RecordingCanvas, clipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
         canvas.drawRect(0, 0, 50, 50, SkPaint());
         canvas.drawRect(50, 50, 100, 100, SkPaint());
@@ -176,16 +176,16 @@
         SkPaint paint;
         paint.setColor(SK_ColorBLUE);
 
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         {
             // a background!
-            canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+            canvas.save(SaveFlags::MatrixClip);
             canvas.drawRect(0, 0, 100, 200, paint);
             canvas.restore();
         }
         {
             // an image!
-            canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+            canvas.save(SaveFlags::MatrixClip);
             canvas.translate(25, 25);
             canvas.scale(2, 2);
             canvas.drawBitmap(bitmap, 0, 0, nullptr);
@@ -224,7 +224,7 @@
 
 TEST(RecordingCanvas, saveLayer_simple) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(10, 20, 190, 180, SkPaint());
         canvas.restore();
     });
@@ -258,7 +258,7 @@
 
 TEST(RecordingCanvas, saveLayer_missingRestore) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         // Note: restore omitted, shouldn't result in unmatched save
     });
@@ -273,7 +273,7 @@
 
 TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SkCanvas::SaveFlags)0); // unclipped
+        canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
         canvas.drawRect(10, 20, 190, 180, SkPaint());
         canvas.restore();
     });
@@ -305,9 +305,9 @@
 
 TEST(RecordingCanvas, saveLayer_addClipFlag) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
-        canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SkCanvas::SaveFlags)0); // unclipped
+        canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
         canvas.drawRect(10, 20, 190, 180, SkPaint());
         canvas.restore();
         canvas.restore();
@@ -327,7 +327,7 @@
         // shouldn't matter, since saveLayer will clip to its bounds
         canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
 
-        canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
@@ -348,12 +348,12 @@
 
 TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.translate(100, 100);
         canvas.rotate(45);
         canvas.translate(-50, -50);
 
-        canvas.saveLayerAlpha(0, 0, 100, 100, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 100, 100, SkPaint());
         canvas.restore();
 
@@ -374,13 +374,13 @@
 
 TEST(RecordingCanvas, saveLayer_rotateClipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
-        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.save(SaveFlags::MatrixClip);
         canvas.translate(100, 100);
         canvas.rotate(45);
         canvas.translate(-200, -200);
 
         // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
-        canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
 
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index 55104de..33209759 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -21,10 +21,19 @@
 
 #include "GLUtils.h"
 
+#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH && !defined(HWUI_GLES_WRAP_ENABLED)
+#error Setting DEBUG_OPENGL to HIGH requires setting HWUI_ENABLE_OPENGL_VALIDATION to true in the Android.mk!
+#endif
+
 namespace android {
 namespace uirenderer {
 
 bool GLUtils::dumpGLErrors() {
+#if DEBUG_OPENGL >= DEBUG_LEVEL_HIGH
+    // If DEBUG_LEVEL_HIGH is set then every GLES call is already wrapped
+    // and asserts that there was no error. So this can just return success.
+    return false;
+#else
     bool errorObserved = false;
     GLenum status = GL_NO_ERROR;
     while ((status = glGetError()) != GL_NO_ERROR) {
@@ -47,6 +56,7 @@
         }
     }
     return errorObserved;
+#endif
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index 85a10f9..b49c1eb 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -23,17 +23,22 @@
 namespace android {
 namespace uirenderer {
 
+
 #if DEBUG_OPENGL
-#define GL_CHECKPOINT() LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\
-        "GL errors! %s:%d", __FILE__, __LINE__)
+#define GL_CHECKPOINT(LEVEL) \
+    do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\
+    LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\
+            "GL errors! %s:%d", __FILE__, __LINE__);\
+    } } while (0)
 #else
-#define GL_CHECKPOINT()
+#define GL_CHECKPOINT(LEVEL)
 #endif
 
 class GLUtils {
 public:
     /**
      * Print out any GL errors with ALOGE, returns true if any errors were found.
+     * You probably want to use GL_CHECKPOINT(LEVEL) instead of calling this directly
      */
     static bool dumpGLErrors();
 
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 504c6d0..9517387 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -389,6 +389,7 @@
         public static final int H264 = 2;
         public static final int MPEG_4_SP = 3;
         public static final int VP8 = 4;
+        public static final int HEVC = 5;
     }
 
     /**
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 86c0e5d..8ef5ca0 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -43,4 +43,10 @@
     void onTimeShiftStatusChanged(int status, int seq);
     void onTimeShiftStartPositionChanged(long timeMs, int seq);
     void onTimeShiftCurrentPositionChanged(long timeMs, int seq);
+
+    // For the recording session
+    void onConnected(int seq);
+    void onRecordingStarted(int seq);
+    void onRecordingStopped(in Uri recordedProgramUri, int seq);
+    void onError(int error, int seq);
 }
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index f8057db..0febc16 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -55,7 +55,8 @@
     void addBlockedRating(in String rating, int userId);
     void removeBlockedRating(in String rating, int userId);
 
-    void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
+    void createSession(in ITvInputClient client, in String inputId, boolean isRecordingSession,
+            int seq, int userId);
     void releaseSession(in IBinder sessionToken, int userId);
 
     void setMainSession(in IBinder sessionToken, int userId);
@@ -77,12 +78,18 @@
 
     void unblockContent(in IBinder sessionToken, in String unblockedRating, int userId);
 
+    void timeShiftPlay(in IBinder sessionToken, in Uri recordedProgramUri, int userId);
     void timeShiftPause(in IBinder sessionToken, int userId);
     void timeShiftResume(in IBinder sessionToken, int userId);
     void timeShiftSeekTo(in IBinder sessionToken, long timeMs, int userId);
     void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
     void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
 
+    // For the recording session
+    void connect(in IBinder sessionToken, in Uri channelUri, in Bundle params, int userId);
+    void startRecording(in IBinder sessionToken, int userId);
+    void stopRecording(in IBinder sessionToken, int userId);
+
     // For TV input hardware binding
     List<TvInputHardwareInfo> getHardwareList();
     ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 6792680..3bf415b 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -16,13 +16,18 @@
 
 package android.media.tv;
 
+import android.media.tv.TvInputInfo;
+
 /**
  * Interface to receive callbacks from ITvInputManager regardless of sessions.
  * @hide
  */
 oneway interface ITvInputManagerCallback {
-    void onInputStateChanged(in String inputId, int state);
     void onInputAdded(in String inputId);
     void onInputRemoved(in String inputId);
     void onInputUpdated(in String inputId);
+
+    void onInputStateChanged(in String inputId, int state);
+
+    void onTvInputInfoChanged(in String inputId, in TvInputInfo TvInputInfo);
 }
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index 7a853d1..bd05184 100644
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -27,10 +27,11 @@
  * @hide
  */
 oneway interface ITvInputService {
-    void registerCallback(ITvInputServiceCallback callback);
+    void registerCallback(in ITvInputServiceCallback callback);
     void unregisterCallback(in ITvInputServiceCallback callback);
-    void createSession(in InputChannel channel, ITvInputSessionCallback callback,
+    void createSession(in InputChannel channel, in ITvInputSessionCallback callback,
             in String inputId);
+    void createRecordingSession(in ITvInputSessionCallback callback, in String inputId);
 
     // For hardware TvInputService
     void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo);
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index 74ab562..9f13882 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -27,4 +27,6 @@
     void addHardwareTvInput(in int deviceId, in TvInputInfo inputInfo);
     void addHdmiTvInput(in int id, in TvInputInfo inputInfo);
     void removeTvInput(in String inputId);
+
+    void setTvInputInfo(in String inputId, in TvInputInfo inputInfo);
 }
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 6a06b8f..408a762 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -48,9 +48,16 @@
 
     void unblockContent(in String unblockedRating);
 
+    void timeShiftPlay(in Uri recordedProgramUri);
     void timeShiftPause();
     void timeShiftResume();
     void timeShiftSeekTo(long timeMs);
     void timeShiftSetPlaybackParams(in PlaybackParams params);
     void timeShiftEnablePositionTracking(boolean enable);
+
+    // For the recording session
+    void connect(in Uri channelUri, in Bundle params);
+    void disconnect();
+    void startRecording();
+    void stopRecording();
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index e936810..cb6a05e 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -40,4 +40,10 @@
     void onTimeShiftStatusChanged(int status);
     void onTimeShiftStartPositionChanged(long timeMs);
     void onTimeShiftCurrentPositionChanged(long timeMs);
+
+    // For the recording session
+    void onConnected();
+    void onRecordingStarted();
+    void onRecordingStopped(in Uri recordedProgramUri);
+    void onError(int error);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index f8c6f3f..4ac5876 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -59,20 +59,29 @@
     private static final int DO_RELAYOUT_OVERLAY_VIEW = 11;
     private static final int DO_REMOVE_OVERLAY_VIEW = 12;
     private static final int DO_UNBLOCK_CONTENT = 13;
-    private static final int DO_TIME_SHIFT_PAUSE = 14;
-    private static final int DO_TIME_SHIFT_RESUME = 15;
-    private static final int DO_TIME_SHIFT_SEEK_TO = 16;
-    private static final int DO_TIME_SHIFT_SET_PLAYBACK_PARAMS = 17;
-    private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 18;
+    private static final int DO_TIME_SHIFT_PLAY = 14;
+    private static final int DO_TIME_SHIFT_PAUSE = 15;
+    private static final int DO_TIME_SHIFT_RESUME = 16;
+    private static final int DO_TIME_SHIFT_SEEK_TO = 17;
+    private static final int DO_TIME_SHIFT_SET_PLAYBACK_PARAMS = 18;
+    private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 19;
+    private static final int DO_CONNECT = 20;
+    private static final int DO_DISCONNECT = 21;
+    private static final int DO_START_RECORDING = 22;
+    private static final int DO_STOP_RECORDING = 23;
 
+    private final boolean mIsRecordingSession;
     private final HandlerCaller mCaller;
 
     private TvInputService.Session mTvInputSessionImpl;
+    private TvInputService.RecordingSession mTvInputRecordingSessionImpl;
+
     private InputChannel mChannel;
     private TvInputEventReceiver mReceiver;
 
     public ITvInputSessionWrapper(Context context, TvInputService.Session sessionImpl,
             InputChannel channel) {
+        mIsRecordingSession = false;
         mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
         mTvInputSessionImpl = sessionImpl;
         mChannel = channel;
@@ -81,9 +90,19 @@
         }
     }
 
+    public ITvInputSessionWrapper(Context context,
+            TvInputService.RecordingSession recordingSessionImpl) {
+        mIsRecordingSession = true;
+        mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
+        mTvInputRecordingSessionImpl = recordingSessionImpl;
+    }
+
     @Override
     public void executeMessage(Message msg) {
-        if (mTvInputSessionImpl == null) {
+        if (!mIsRecordingSession && mTvInputSessionImpl == null) {
+            return;
+        }
+        if (mIsRecordingSession && mTvInputRecordingSessionImpl == null) {
             return;
         }
 
@@ -138,7 +157,12 @@
             }
             case DO_APP_PRIVATE_COMMAND: {
                 SomeArgs args = (SomeArgs) msg.obj;
-                mTvInputSessionImpl.appPrivateCommand((String) args.arg1, (Bundle) args.arg2);
+                if (mIsRecordingSession) {
+                    mTvInputRecordingSessionImpl.appPrivateCommand(
+                            (String) args.arg1, (Bundle) args.arg2);
+                } else {
+                    mTvInputSessionImpl.appPrivateCommand((String) args.arg1, (Bundle) args.arg2);
+                }
                 args.recycle();
                 break;
             }
@@ -160,6 +184,10 @@
                 mTvInputSessionImpl.unblockContent((String) msg.obj);
                 break;
             }
+            case DO_TIME_SHIFT_PLAY: {
+                mTvInputSessionImpl.timeShiftPlay((Uri) msg.obj);
+                break;
+            }
             case DO_TIME_SHIFT_PAUSE: {
                 mTvInputSessionImpl.timeShiftPause();
                 break;
@@ -180,6 +208,25 @@
                 mTvInputSessionImpl.timeShiftEnablePositionTracking((Boolean) msg.obj);
                 break;
             }
+            case DO_CONNECT: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                mTvInputRecordingSessionImpl.connect((Uri) args.arg1, (Bundle) args.arg2);
+                args.recycle();
+                break;
+            }
+            case DO_DISCONNECT: {
+                mTvInputRecordingSessionImpl.disconnect();
+                mTvInputRecordingSessionImpl = null;
+                break;
+            }
+            case DO_START_RECORDING: {
+                mTvInputRecordingSessionImpl.startRecording();
+                break;
+            }
+            case DO_STOP_RECORDING: {
+                mTvInputRecordingSessionImpl.stopRecording();
+                break;
+            }
             default: {
                 Log.w(TAG, "Unhandled message code: " + msg.what);
                 break;
@@ -274,6 +321,12 @@
     }
 
     @Override
+    public void timeShiftPlay(Uri recordedProgramUri) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(
+                DO_TIME_SHIFT_PLAY, recordedProgramUri));
+    }
+
+    @Override
     public void timeShiftPause() {
         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_TIME_SHIFT_PAUSE));
     }
@@ -300,6 +353,28 @@
                 DO_TIME_SHIFT_ENABLE_POSITION_TRACKING, enable));
     }
 
+    @Override
+    public void connect(Uri channelUri, Bundle params) {
+        // Clear the pending connect requests.
+        mCaller.removeMessages(DO_CONNECT);
+        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CONNECT, channelUri, params));
+    }
+
+    @Override
+    public void disconnect() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_DISCONNECT));
+    }
+
+    @Override
+    public void startRecording() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_RECORDING));
+    }
+
+    @Override
+    public void stopRecording() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_STOP_RECORDING));
+    }
+
     private final class TvInputEventReceiver extends InputEventReceiver {
         public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 7cd086e..62a01dc 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -54,6 +54,7 @@
 
     private static final String PATH_CHANNEL = "channel";
     private static final String PATH_PROGRAM = "program";
+    private static final String PATH_RECORDED_PROGRAM = "recorded_program";
     private static final String PATH_PASSTHROUGH = "passthrough";
 
     /**
@@ -273,6 +274,15 @@
     }
 
     /**
+     * Builds a URI that points to a specific recorded program.
+     *
+     * @param recordedProgramId The ID of the recorded program to point to.
+     */
+    public static final Uri buildRecordedProgramUri(long recordedProgramId) {
+        return ContentUris.withAppendedId(RecordedPrograms.CONTENT_URI, recordedProgramId);
+    }
+
+    /**
      * Builds a URI that points to a specific program the user watched.
      *
      * @param watchedProgramId The ID of the watched program to point to.
@@ -941,6 +951,8 @@
          *
          * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
          *
+         * <p>This is a required field.
+         *
          * <p>Type: INTEGER (long)
          */
         public static final String COLUMN_CHANNEL_ID = "channel_id";
@@ -1336,6 +1348,382 @@
     }
 
     /**
+     * Column definitions for the recorded TV programs table.
+     *
+     * <p>By default, the query results will be sorted by {@link #COLUMN_START_TIME_UTC_MILLIS} in
+     * ascending order.
+     */
+    public static final class RecordedPrograms implements BaseTvColumns {
+
+        /** The content:// style URI for this table. */
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+                + PATH_RECORDED_PROGRAM);
+
+        /** The MIME type of a directory of recorded TV programs. */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/recorded_program";
+
+        /** The MIME type of a single recorded TV program. */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/recorded_program";
+
+        /**
+         * The ID of the TV channel that provided this recorded TV program.
+         *
+         * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
+         *
+         * <p>This is a required field.
+         *
+         * <p>Type: INTEGER (long)
+         * @see Programs#COLUMN_CHANNEL_ID
+         */
+        public static final String COLUMN_CHANNEL_ID = Programs.COLUMN_CHANNEL_ID;
+
+        /**
+         * The title of this recorded TV program.
+         *
+         * <p>If this recorded program is an episodic TV show, it is recommended that the title is
+         * the series title and its related fields ({@link #COLUMN_SEASON_NUMBER},
+         * {@link #COLUMN_EPISODE_NUMBER}, and {@link #COLUMN_EPISODE_TITLE}) are filled in.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_TITLE
+         */
+        public static final String COLUMN_TITLE = Programs.COLUMN_TITLE;
+
+        /**
+         * The season number of this recorded TV program for episodic TV shows.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_SEASON_NUMBER
+         */
+        public static final String COLUMN_SEASON_NUMBER = Programs.COLUMN_SEASON_NUMBER;
+
+        /**
+         * The episode number of this recorded TV program for episodic TV shows.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_EPISODE_NUMBER
+         */
+        public static final String COLUMN_EPISODE_NUMBER = Programs.COLUMN_EPISODE_NUMBER;
+
+        /**
+         * The episode title of this recorded TV program for episodic TV shows.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_EPISODE_TITLE
+         */
+        public static final String COLUMN_EPISODE_TITLE = Programs.COLUMN_EPISODE_TITLE;
+
+        /**
+         * The start time of the original TV program, in milliseconds since the epoch.
+         *
+         * <p>Type: INTEGER (long)
+         * @see Programs#COLUMN_START_TIME_UTC_MILLIS
+         */
+        public static final String COLUMN_START_TIME_UTC_MILLIS =
+                Programs.COLUMN_START_TIME_UTC_MILLIS;
+
+        /**
+         * The end time of the original TV program, in milliseconds since the epoch.
+         *
+         * <p>Type: INTEGER (long)
+         * @see Programs#COLUMN_END_TIME_UTC_MILLIS
+         */
+        public static final String COLUMN_END_TIME_UTC_MILLIS = Programs.COLUMN_END_TIME_UTC_MILLIS;
+
+        /**
+         * The comma-separated genre string of this recorded TV program.
+         *
+         * <p>Use the same language appeared in the underlying broadcast standard, if applicable.
+         * (For example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+         * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, leave empty.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_BROADCAST_GENRE
+         */
+        public static final String COLUMN_BROADCAST_GENRE = Programs.COLUMN_BROADCAST_GENRE;
+
+        /**
+         * The comma-separated canonical genre string of this recorded TV program.
+         *
+         * <p>Canonical genres are defined in {@link Programs.Genres}. Use
+         * {@link Programs.Genres#encode Genres.encode()} to create a text that can be stored in
+         * this column. Use {@link Programs.Genres#decode Genres.decode()} to get the canonical
+         * genre strings from the text stored in this column.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_CANONICAL_GENRE
+         * @see Programs.Genres
+         */
+        public static final String COLUMN_CANONICAL_GENRE = Programs.COLUMN_CANONICAL_GENRE;
+
+        /**
+         * The short description of this recorded TV program that is displayed to the user by
+         * default.
+         *
+         * <p>It is recommended to limit the length of the descriptions to 256 characters.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_SHORT_DESCRIPTION
+         */
+        public static final String COLUMN_SHORT_DESCRIPTION = Programs.COLUMN_SHORT_DESCRIPTION;
+
+        /**
+         * The detailed, lengthy description of this recorded TV program that is displayed only when
+         * the user wants to see more information.
+         *
+         * <p>TV input services should leave this field empty if they have no additional details
+         * beyond {@link #COLUMN_SHORT_DESCRIPTION}.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_LONG_DESCRIPTION
+         */
+        public static final String COLUMN_LONG_DESCRIPTION = Programs.COLUMN_LONG_DESCRIPTION;
+
+        /**
+         * The width of the video for this recorded TV program, in the unit of pixels.
+         *
+         * <p>Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video
+         * resolution of the current recorded TV program. Can be empty if it is not known or the
+         * recorded program does not convey any video.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_VIDEO_WIDTH
+         */
+        public static final String COLUMN_VIDEO_WIDTH = Programs.COLUMN_VIDEO_WIDTH;
+
+        /**
+         * The height of the video for this recorded TV program, in the unit of pixels.
+         *
+         * <p>Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video
+         * resolution of the current recorded TV program. Can be empty if it is not known or the
+         * recorded program does not convey any video.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_VIDEO_HEIGHT
+         */
+        public static final String COLUMN_VIDEO_HEIGHT = Programs.COLUMN_VIDEO_HEIGHT;
+
+        /**
+         * The comma-separated audio languages of this recorded TV program.
+         *
+         * <p>This is used to describe available audio languages included in the recorded program.
+         * Use either ISO 639-1 or 639-2/T codes.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_AUDIO_LANGUAGE
+         */
+        public static final String COLUMN_AUDIO_LANGUAGE = Programs.COLUMN_AUDIO_LANGUAGE;
+
+        /**
+         * The comma-separated content ratings of this recorded TV program.
+         *
+         * <p>This is used to describe the content rating(s) of this recorded program. Each
+         * comma-separated content rating sub-string should be generated by calling
+         * {@link TvContentRating#flattenToString}. Note that in most cases the recorded program
+         * content is rated by a single rating system, thus resulting in a corresponding single
+         * sub-string that does not require comma separation and multiple sub-strings appear only
+         * when the recorded program content is rated by two or more content rating systems. If any
+         * of those ratings is specified as "blocked rating" in the user's parental control
+         * settings, the TV input service should block the current content and wait for the signal
+         * that it is okay to unblock.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_CONTENT_RATING
+         */
+        public static final String COLUMN_CONTENT_RATING = Programs.COLUMN_CONTENT_RATING;
+
+        /**
+         * The URI for the poster art of this recorded TV program.
+         *
+         * <p>The data in the column must be a URL, or a URI in one of the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_POSTER_ART_URI
+         */
+        public static final String COLUMN_POSTER_ART_URI = Programs.COLUMN_POSTER_ART_URI;
+
+        /**
+         * The URI for the thumbnail of this recorded TV program.
+         *
+         * <p>The system can generate a thumbnail from the poster art if this column is not
+         * specified. Thus it is not necessary for TV input services to include a thumbnail if it is
+         * just a scaled image of the poster art.
+         *
+         * <p>The data in the column must be a URL, or a URI in one of the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_THUMBNAIL_URI
+         */
+        public static final String COLUMN_THUMBNAIL_URI = Programs.COLUMN_THUMBNAIL_URI;
+
+        /**
+         * The flag indicating whether this recorded TV program is searchable or not.
+         *
+         * <p>The columns of searchable recorded programs can be read by other applications that
+         * have proper permission. Care must be taken not to open sensitive data.
+         *
+         * <p>A value of 1 indicates that the recorded program is searchable and its columns can be
+         * read by other applications, a value of 0 indicates that the recorded program is hidden
+         * and its columns can be read only by the package that owns the recorded program and the
+         * system. If not specified, this value is set to 1 (searchable) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         * @see Programs#COLUMN_SEARCHABLE
+         */
+        public static final String COLUMN_SEARCHABLE = Programs.COLUMN_SEARCHABLE;
+
+        /**
+         * The URI of the recording data for this recorded program.
+         *
+         * <p>Together with {@link #COLUMN_RECORDING_DATA_BYTES}, applications can use this
+         * information to manage recording storage. The URI should indicate a file or directory with
+         * the scheme {@link android.content.ContentResolver#SCHEME_FILE}.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_RECORDING_DATA_BYTES
+         */
+        public static final String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
+
+        /**
+         * The data size (in bytes) for this recorded program.
+         *
+         * <p>Together with {@link #COLUMN_RECORDING_DATA_URI}, applications can use this
+         * information to manage recording storage.
+         *
+         * <p>Type: INTEGER (long)
+         * @see #COLUMN_RECORDING_DATA_URI
+         */
+        public static final String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
+
+        /**
+         * The duration (in milliseconds) of this recorded program.
+         *
+         * <p>The actual duration of the recorded program can differ from the one calculated by
+         * {@link #COLUMN_END_TIME_UTC_MILLIS} - {@link #COLUMN_START_TIME_UTC_MILLIS} as program
+         * recording can be interrupted in the middle for some reason, resulting in a partially
+         * recorded program, which is still playable.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_RECORDING_DURATION_MILLIS = "recording_duration_millis";
+
+        /**
+         * The expiration time for this recorded program, in milliseconds since the epoch.
+         *
+         * <p>Recorded TV programs do not expire by default unless explicitly requested by the user
+         * or the user allows applications to delete them in order to free up disk space for future
+         * recording. However, some TV content can have expiration date set by the content provider
+         * when recorded. This field is used to indicate such a restriction.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER (long)
+         */
+        public static final String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS =
+                "recording_expire_time_utc_millis";
+
+
+        /**
+         * Internal data used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: BLOB
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_DATA
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_DATA =
+                Programs.COLUMN_INTERNAL_PROVIDER_DATA;
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG1
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 =
+                Programs.COLUMN_INTERNAL_PROVIDER_FLAG1;
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG2
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 =
+                Programs.COLUMN_INTERNAL_PROVIDER_FLAG2;
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG3
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 =
+                Programs.COLUMN_INTERNAL_PROVIDER_FLAG3;
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_INTERNAL_PROVIDER_FLAG4
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 =
+                Programs.COLUMN_INTERNAL_PROVIDER_FLAG4;
+
+        /**
+         * The version number of this row entry used by TV input services.
+         *
+         * <p>This is best used by sync adapters to identify the rows to update. The number can be
+         * defined by individual TV input services. One may assign the same value as
+         * {@code version_number} in ETSI EN 300 468 or ATSC A/65, if the data are coming from a TV
+         * broadcast.
+         *
+         * <p>Type: INTEGER
+         * @see Programs#COLUMN_VERSION_NUMBER
+         */
+        public static final String COLUMN_VERSION_NUMBER = Programs.COLUMN_VERSION_NUMBER;
+
+        private RecordedPrograms() {}
+    }
+
+    /**
      * Column definitions for the TV programs that the user watched. Applications do not have access
      * to this table.
      *
@@ -1376,6 +1764,8 @@
         /**
          * The ID of the TV channel that provides this TV program.
          *
+         * <p>This is a required field.
+         *
          * <p>Type: INTEGER (long)
          */
         public static final String COLUMN_CHANNEL_ID = "channel_id";
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index a3d748e..3960230 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -419,6 +419,27 @@
     }
 
     /**
+     * Returns the number of tuners this TV input has.
+     *
+     * <p>This method is valid only for the input of type {@link #TYPE_TUNER}.
+     *
+     * <p>Tuners correspond to physical/logical resources that allow reception of TV signal. Having
+     * <i>N</i> tuners means that the TV input is capable of receiving <i>N</i> different channels
+     * concurrently.
+     *
+     */
+    public int getTunerCount() {
+        return mType == TYPE_TUNER ? 1 : 0;
+    }
+
+    /**
+     * Returns {@code true} if this TV input can record TV programs, {@code false} otherwise.
+     */
+    public boolean canRecord() {
+        return false;
+    }
+
+    /**
      * Returns the HDMI device information of this TV input.
      * @hide
      */
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 6a13f82..f1de8fd 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -44,6 +45,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -126,6 +129,35 @@
     public static final long TIME_SHIFT_INVALID_TIME = Long.MIN_VALUE;
 
     /**
+     * RecordingError when a requested operation cannot be completed due to a problem that does not
+     * fit under any other error code.
+     */
+    public static final int RECORDING_ERROR_UNKNOWN = 0;
+
+    /**
+     * RecordingError when an attempt to connect to a recording session has failed or the
+     * established connection has been disconnected without a known reason.
+     */
+    public static final int RECORDING_ERROR_CONNECTION_FAILED = 1;
+
+    /**
+     * RecordingError when recording cannot proceed due to insufficient storage space.
+     */
+    public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 2;
+
+    /**
+     * RecordingError when recording cannot proceed because the required recording resource is not
+     * able to be allocated.
+     */
+    public static final int RECORDING_ERROR_RESOURCE_BUSY = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_CONNECTION_FAILED,
+            RECORDING_ERROR_INSUFFICIENT_SPACE, RECORDING_ERROR_RESOURCE_BUSY})
+    public @interface RecordingError {}
+
+    /**
      * The TV input is connected.
      *
      * <p>This state indicates that a source device is connected to the input port and is in the
@@ -416,6 +448,39 @@
          */
         public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) {
         }
+
+        /**
+         * This is called when a recording session initiated by a call to {@link
+         * TvRecordingClient#connect(String, Uri)} has been established.
+         */
+        void onConnected(Session session) {
+        }
+
+        /**
+         * This is called when TV program recording on the current channel has started.
+         *
+         * @param session A {@link TvInputManager.Session} associated with this callback.
+         */
+        void onRecordingStarted(Session session) {
+        }
+
+        /**
+         * This is called when TV program recording on the current channel has stopped. The passed
+         * URI contains information about the new recorded program.
+         *
+         * @param recordedProgramUri The URI for the new recorded program.
+         * @see android.media.tv.TvContract.RecordedPrograms
+         **/
+        void onRecordingStopped(Session session, Uri recordedProgramUri) {
+        }
+
+        /**
+         * This is called when an issue has occurred before or during recording.
+         *
+         * @param error The error code.
+         */
+        void onError(Session session, @TvInputManager.RecordingError int error) {
+        }
     }
 
     private static final class SessionCallbackRecord {
@@ -565,6 +630,46 @@
                 }
             });
         }
+
+        // For the recording session only
+        void postConnected() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onConnected(mSession);
+                }
+            });
+        }
+
+        // For the recording session only
+        void postRecordingStarted() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRecordingStarted(mSession);
+                }
+            });
+        }
+
+        // For the recording session only
+        void postRecordingStopped(final Uri recordedProgramUri) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onRecordingStopped(mSession, recordedProgramUri);
+                }
+            });
+        }
+
+        // For the recording session only
+        void postError(final int error) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onError(mSession, error);
+                }
+            });
+        }
     }
 
     /**
@@ -574,7 +679,7 @@
         /**
          * This is called when the state of a given TV input is changed.
          *
-         * @param inputId The id of the TV input.
+         * @param inputId The ID of the TV input.
          * @param state State of the TV input. The value is one of the following:
          * <ul>
          * <li>{@link TvInputManager#INPUT_STATE_CONNECTED}
@@ -591,7 +696,7 @@
          * <p>Normally it happens when the user installs a new TV input package that implements
          * {@link TvInputService} interface.
          *
-         * @param inputId The id of the TV input.
+         * @param inputId The ID of the TV input.
          */
         public void onInputAdded(String inputId) {
         }
@@ -602,7 +707,7 @@
          * <p>Normally it happens when the user uninstalls the previously installed TV input
          * package.
          *
-         * @param inputId The id of the TV input.
+         * @param inputId The ID of the TV input.
          */
         public void onInputRemoved(String inputId) {
         }
@@ -613,12 +718,21 @@
          * <p>Normally it happens when a previously installed TV input package is re-installed or
          * the media on which a newer version of the package exists becomes available/unavailable.
          *
-         * @param inputId The id of the TV input.
+         * @param inputId The ID of the TV input.
          * @hide
          */
         @SystemApi
         public void onInputUpdated(String inputId) {
         }
+
+        /**
+         * This is called when the information about a given TV input is changed.
+         *
+         * @param inputId The ID of the TV input.
+         * @param inputInfo TvInputInfo object that contains the information about the TV input.
+         */
+        public void onTvInputInfoChanged(String inputId, TvInputInfo inputInfo) {
+        }
     }
 
     private static final class TvInputCallbackRecord {
@@ -634,15 +748,6 @@
             return mCallback;
         }
 
-        public void postInputStateChanged(final String inputId, final int state) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mCallback.onInputStateChanged(inputId, state);
-                }
-            });
-        }
-
         public void postInputAdded(final String inputId) {
             mHandler.post(new Runnable() {
                 @Override
@@ -669,6 +774,24 @@
                 }
             });
         }
+
+        public void postInputStateChanged(final String inputId, final int state) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onInputStateChanged(inputId, state);
+                }
+            });
+        }
+
+        public void postTvInputInfoChanged(final String inputId, final TvInputInfo inputInfo) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onTvInputInfoChanged(inputId, inputInfo);
+                }
+            });
+        }
     }
 
     /**
@@ -876,19 +999,57 @@
                     record.postTimeShiftCurrentPositionChanged(timeMs);
                 }
             }
-        };
-        ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
+
             @Override
-            public void onInputStateChanged(String inputId, int state) {
-                synchronized (mLock) {
-                    mStateMap.put(inputId, state);
-                    for (TvInputCallbackRecord record : mCallbackRecords) {
-                        record.postInputStateChanged(inputId, state);
+            public void onConnected(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
                     }
+                    record.postConnected();
                 }
             }
 
             @Override
+            public void onRecordingStarted(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRecordingStarted();
+                }
+            }
+
+            @Override
+            public void onRecordingStopped(Uri recordedProgramUri, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postRecordingStopped(recordedProgramUri);
+                }
+            }
+
+            @Override
+            public void onError(int error, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postError(error);
+                }
+            }
+        };
+        ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
+            @Override
             public void onInputAdded(String inputId) {
                 synchronized (mLock) {
                     mStateMap.put(inputId, INPUT_STATE_CONNECTED);
@@ -916,6 +1077,25 @@
                     }
                 }
             }
+
+            @Override
+            public void onInputStateChanged(String inputId, int state) {
+                synchronized (mLock) {
+                    mStateMap.put(inputId, state);
+                    for (TvInputCallbackRecord record : mCallbackRecords) {
+                        record.postInputStateChanged(inputId, state);
+                    }
+                }
+            }
+
+            @Override
+            public void onTvInputInfoChanged(String inputId, TvInputInfo inputInfo) {
+                synchronized (mLock) {
+                    for (TvInputCallbackRecord record : mCallbackRecords) {
+                        record.postTvInputInfoChanged(inputId, inputInfo);
+                    }
+                }
+            }
         };
         try {
             if (mService != null) {
@@ -972,7 +1152,7 @@
      * <li>{@link #INPUT_STATE_DISCONNECTED}
      * </ul>
      *
-     * @param inputId The id of the TV input.
+     * @param inputId The ID of the TV input.
      * @throws IllegalArgumentException if the argument is {@code null}.
      */
     public int getInputState(@NonNull String inputId) {
@@ -1139,7 +1319,7 @@
      * <p>The number of sessions that can be created at the same time is limited by the capability
      * of the given TV input.
      *
-     * @param inputId The id of the TV input.
+     * @param inputId The ID of the TV input.
      * @param callback A callback used to receive the created session.
      * @param handler A {@link Handler} that the session creation will be delivered to.
      * @hide
@@ -1147,6 +1327,28 @@
     @SystemApi
     public void createSession(@NonNull String inputId, @NonNull final SessionCallback callback,
             @NonNull Handler handler) {
+        createSessionInternal(inputId, false, callback, handler);
+    }
+
+    /**
+     * Creates a recording {@link Session} for a given TV input.
+     *
+     * <p>The number of sessions that can be created at the same time is limited by the capability
+     * of the given TV input.
+     *
+     * @param inputId The ID of the TV input.
+     * @param callback A callback used to receive the created session.
+     * @param handler A {@link Handler} that the session creation will be delivered to.
+     * @hide
+     */
+    @SystemApi
+    public void createRecordingSession(@NonNull String inputId,
+            @NonNull final SessionCallback callback, @NonNull Handler handler) {
+        createSessionInternal(inputId, true, callback, handler);
+    }
+
+    private void createSessionInternal(String inputId, boolean isRecordingSession,
+            SessionCallback callback, Handler handler) {
         Preconditions.checkNotNull(inputId);
         Preconditions.checkNotNull(callback);
         Preconditions.checkNotNull(handler);
@@ -1155,7 +1357,7 @@
             int seq = mNextSeq++;
             mSessionCallbackRecordMap.put(seq, record);
             try {
-                mService.createSession(mClient, inputId, seq, mUserId);
+                mService.createSession(mClient, inputId, isRecordingSession, seq, mUserId);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -1171,7 +1373,7 @@
      * here. This method is designed to be used with {@link #captureFrame} in
      * capture scenarios specifically and not suitable for any other use.
      *
-     * @param inputId the id of the TV input.
+     * @param inputId The ID of the TV input.
      * @return List of {@link TvStreamConfig} which is available for capturing
      *   of the given TV input.
      * @hide
@@ -1188,7 +1390,7 @@
     /**
      * Take a snapshot of the given TV input into the provided Surface.
      *
-     * @param inputId the id of the TV input.
+     * @param inputId The ID of the TV input.
      * @param surface the {@link Surface} to which the snapshot is captured.
      * @param config the {@link TvStreamConfig} which is used for capturing.
      * @return true when the {@link Surface} is ready to be captured.
@@ -1607,7 +1809,7 @@
          * Returns the selected track for a given type. Returns {@code null} if the information is
          * not available or any of the tracks for the given type is not selected.
          *
-         * @return the ID of the selected track.
+         * @return The ID of the selected track.
          * @see #selectTrack
          */
         @Nullable
@@ -1697,6 +1899,21 @@
         }
 
         /**
+         * Plays a given recorded TV program.
+         */
+        void timeShiftPlay(Uri recordedProgramUri) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.timeShiftPlay(mToken, recordedProgramUri, mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
          * Pauses the playback. Call {@link #timeShiftResume()} to restart the playback.
          */
         void timeShiftPause() {
@@ -1782,6 +1999,62 @@
         }
 
         /**
+         * Connects to a given channel for TV program recording.
+         */
+        void connect(Uri channelUri) {
+            connect(channelUri, null);
+        }
+
+        /**
+         * Tunes to a given channel.
+         *
+         * @param channelUri The URI of a channel.
+         * @param params Extra parameters.
+         */
+        void connect(@NonNull Uri channelUri, Bundle params) {
+            Preconditions.checkNotNull(channelUri);
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.connect(mToken, channelUri, params, mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
+         * Starts TV program recording for the current recording session.
+         */
+        void startRecording() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.startRecording(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
+         * Stops TV program recording for the current recording session.
+         */
+        void stopRecording() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.stopRecording(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
          * Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle)
          * TvInputService.Session.appPrivateCommand()} on the current TvView.
          *
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 053d43b6..f74ae66 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -136,6 +137,18 @@
             }
 
             @Override
+            public void createRecordingSession(ITvInputSessionCallback cb, String inputId) {
+                if (cb == null) {
+                    return;
+                }
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = cb;
+                args.arg2 = inputId;
+                mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_RECORDING_SESSION, args)
+                        .sendToTarget();
+            }
+
+            @Override
             public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) {
                 mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_TV_INPUT,
                         hardwareInfo).sendToTarget();
@@ -174,6 +187,19 @@
     public abstract Session onCreateSession(String inputId);
 
     /**
+     * Returns a concrete implementation of {@link RecordingSession}.
+     *
+     * <p>May return {@code null} if this TV input service fails to create a recording session for
+     * some reason.
+     *
+     * @param inputId The ID of the TV input associated with the recording session.
+     */
+    @Nullable
+    public RecordingSession onCreateRecordingSession(String inputId) {
+        return null;
+    }
+
+    /**
      * Returns a new {@link TvInputInfo} object if this service is responsible for
      * {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of
      * ignoring all hardware input.
@@ -229,6 +255,25 @@
         return null;
     }
 
+
+    /**
+     * Sets the TvInputInfo for this TV input.
+     *
+     * <p>The system service automatically creates the TvInputInfo for each TV input based on
+     * information collected from the AndroidManifest.xml, thus it is not necessary to call this
+     * method unless the TV input has additional information to pass such as ability to record and
+     * tuner count.
+     *
+     * @param inputId The ID of the TV input.
+     * @param inputInfo The TvInputInfo object that contains that new information.
+     */
+    public final void setTvInputInfo(String inputId, TvInputInfo inputInfo) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = inputId;
+        args.arg2 = inputInfo;
+        mServiceHandler.obtainMessage(ServiceHandler.DO_SET_TV_INPUT_INFO, args).sendToTarget();
+    }
+
     private boolean isPassthroughInput(String inputId) {
         if (mTvInputManager == null) {
             mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
@@ -322,7 +367,7 @@
         @SystemApi
         public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) {
             Preconditions.checkNotNull(eventType);
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
                 @Override
                 public void run() {
                     try {
@@ -347,7 +392,8 @@
          * @param channelUri The URI of the new channel.
          */
         public void notifyChannelRetuned(final Uri channelUri) {
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -387,7 +433,8 @@
 
             // TODO: Validate the track list.
             final List<TvTrackInfo> tracksCopy = new ArrayList<>(tracks);
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -417,7 +464,8 @@
          * @see #onSelectTrack
          */
         public void notifyTrackSelected(final int type, final String trackId) {
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -443,7 +491,8 @@
          * @see #notifyVideoUnavailable
          */
         public void notifyVideoAvailable() {
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -478,7 +527,8 @@
                     || reason > TvInputManager.VIDEO_UNAVAILABLE_REASON_END) {
                 throw new IllegalArgumentException("Unknown reason: " + reason);
             }
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -518,7 +568,8 @@
          * @see TvInputManager
          */
         public void notifyContentAllowed() {
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -562,7 +613,8 @@
          */
         public void notifyContentBlocked(@NonNull final TvContentRating rating) {
             Preconditions.checkNotNull(rating);
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -603,7 +655,8 @@
          * </ul>
          */
         public void notifyTimeShiftStatusChanged(final int status) {
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -619,7 +672,8 @@
         }
 
         private void notifyTimeShiftStartPositionChanged(final long timeMs) {
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -635,7 +689,8 @@
         }
 
         private void notifyTimeShiftCurrentPositionChanged(final long timeMs) {
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -665,7 +720,8 @@
             if (left > right || top > bottom) {
                 throw new IllegalArgumentException("Invalid parameter");
             }
-            executeOrPostRunnable(new Runnable() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
                 @Override
                 public void run() {
                     try {
@@ -858,13 +914,28 @@
         }
 
         /**
+         * Called when the application requests to play a given recorded TV program.
+         *
+         * @param recordedProgramUri The URI of a recorded TV program.
+         * @see #onTimeShiftResume()
+         * @see #onTimeShiftPause()
+         * @see #onTimeShiftSeekTo(long)
+         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+         * @see #onTimeShiftGetStartPosition()
+         * @see #onTimeShiftGetCurrentPosition()
+         */
+        public void onTimeShiftPlay(Uri recordedProgramUri) {
+        }
+
+        /**
          * Called when the application requests to pause playback.
          *
-         * @see #onTimeShiftResume
-         * @see #onTimeShiftSeekTo
-         * @see #onTimeShiftSetPlaybackParams
-         * @see #onTimeShiftGetStartPosition
-         * @see #onTimeShiftGetCurrentPosition
+         * @see #onTimeShiftPlay(Uri)
+         * @see #onTimeShiftResume()
+         * @see #onTimeShiftSeekTo(long)
+         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+         * @see #onTimeShiftGetStartPosition()
+         * @see #onTimeShiftGetCurrentPosition()
          */
         public void onTimeShiftPause() {
         }
@@ -872,11 +943,12 @@
         /**
          * Called when the application requests to resume playback.
          *
-         * @see #onTimeShiftPause
-         * @see #onTimeShiftSeekTo
-         * @see #onTimeShiftSetPlaybackParams
-         * @see #onTimeShiftGetStartPosition
-         * @see #onTimeShiftGetCurrentPosition
+         * @see #onTimeShiftPlay(Uri)
+         * @see #onTimeShiftPause()
+         * @see #onTimeShiftSeekTo(long)
+         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+         * @see #onTimeShiftGetStartPosition()
+         * @see #onTimeShiftGetCurrentPosition()
          */
         public void onTimeShiftResume() {
         }
@@ -888,11 +960,12 @@
          * not in the range.
          *
          * @param timeMs The time position to seek to, in milliseconds since the epoch.
-         * @see #onTimeShiftResume
-         * @see #onTimeShiftPause
-         * @see #onTimeShiftSetPlaybackParams
-         * @see #onTimeShiftGetStartPosition
-         * @see #onTimeShiftGetCurrentPosition
+         * @see #onTimeShiftPlay(Uri)
+         * @see #onTimeShiftResume()
+         * @see #onTimeShiftPause()
+         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+         * @see #onTimeShiftGetStartPosition()
+         * @see #onTimeShiftGetCurrentPosition()
          */
         public void onTimeShiftSeekTo(long timeMs) {
         }
@@ -905,11 +978,12 @@
          * parameters previously set.
          *
          * @param params The playback params.
-         * @see #onTimeShiftResume
-         * @see #onTimeShiftPause
-         * @see #onTimeShiftSeekTo
-         * @see #onTimeShiftGetStartPosition
-         * @see #onTimeShiftGetCurrentPosition
+         * @see #onTimeShiftPlay(Uri)
+         * @see #onTimeShiftResume()
+         * @see #onTimeShiftPause()
+         * @see #onTimeShiftSeekTo(long)
+         * @see #onTimeShiftGetStartPosition()
+         * @see #onTimeShiftGetCurrentPosition()
          */
         public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
         }
@@ -925,11 +999,12 @@
          * seek to, thus failure to notifying its change immediately might result in bad experience
          * where the application allows the user to seek to an invalid time position.
          *
-         * @see #onTimeShiftResume
-         * @see #onTimeShiftPause
-         * @see #onTimeShiftSeekTo
-         * @see #onTimeShiftSetPlaybackParams
-         * @see #onTimeShiftGetCurrentPosition
+         * @see #onTimeShiftPlay(Uri)
+         * @see #onTimeShiftResume()
+         * @see #onTimeShiftPause()
+         * @see #onTimeShiftSeekTo(long)
+         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+         * @see #onTimeShiftGetCurrentPosition()
          */
         public long onTimeShiftGetStartPosition() {
             return TvInputManager.TIME_SHIFT_INVALID_TIME;
@@ -944,11 +1019,12 @@
          * playback position reported by {@link #onTimeShiftGetStartPosition}. Failure to notifying
          * the correct current position might lead to bad user experience.
          *
-         * @see #onTimeShiftResume
-         * @see #onTimeShiftPause
-         * @see #onTimeShiftSeekTo
-         * @see #onTimeShiftSetPlaybackParams
-         * @see #onTimeShiftGetStartPosition
+         * @see #onTimeShiftPlay(Uri)
+         * @see #onTimeShiftResume()
+         * @see #onTimeShiftPause()
+         * @see #onTimeShiftSeekTo(long)
+         * @see #onTimeShiftSetPlaybackParams(PlaybackParams)
+         * @see #onTimeShiftGetStartPosition()
          */
         public long onTimeShiftGetCurrentPosition() {
             return TvInputManager.TIME_SHIFT_INVALID_TIME;
@@ -1263,6 +1339,14 @@
         }
 
         /**
+         * Calls {@link #onTimeShiftPlay(Uri)}.
+         */
+        void timeShiftPlay(Uri recordedProgramUri) {
+            mCurrentPositionMs = 0;
+            onTimeShiftPlay(recordedProgramUri);
+        }
+
+        /**
          * Calls {@link #onTimeShiftPause}.
          */
         void timeShiftPause() {
@@ -1385,7 +1469,7 @@
             }
         }
 
-        private void executeOrPostRunnable(Runnable action) {
+        private void executeOrPostRunnableOnMainThread(Runnable action) {
             synchronized(mLock) {
                 if (mSessionCallback == null) {
                     // The session is not initialized yet.
@@ -1449,6 +1533,267 @@
     }
 
     /**
+     * Base class for derived classes to implement to provide a TV input recording session.
+     */
+    public abstract static class RecordingSession {
+        final Handler mHandler;
+
+        private final Object mLock = new Object();
+        // @GuardedBy("mLock")
+        private ITvInputSessionCallback mSessionCallback;
+        // @GuardedBy("mLock")
+        private final List<Runnable> mPendingActions = new ArrayList<>();
+
+        /**
+         * Creates a new Recording Session for TV program recording.
+         *
+         * @param context The context of the application
+         */
+        public RecordingSession(Context context) {
+            mHandler = new Handler(context.getMainLooper());
+        }
+
+        /**
+         * Informs the application that recording session has been connected.
+         */
+        public void notifyConnected() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifyConnected");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onConnected();
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyConnected", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Informs the application that recording has started.
+         */
+        public void notifyRecordingStarted() {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifyRecordingStarted");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRecordingStarted();
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyRecordingStarted", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Informs the application that recording has stopped successfully. Each TV input service
+         * should create a new data entry in the recorded programs table upon completion of the
+         * recording and send its URI.
+         *
+         * @param recordedProgramUri The URI of the new recorded program.
+         */
+        public void notifyRecordingStopped(final Uri recordedProgramUri) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifyRecordingStopped");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onRecordingStopped(recordedProgramUri);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyRecordingStopped", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Sends an error to the application at any moment.
+         *
+         * @param error The error code. Should be one of the followings.
+         * <ul>
+         * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
+         * <li>{@link TvInputManager#RECORDING_ERROR_CONNECTION_FAILED}
+         * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
+         * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
+         * </ul>
+         */
+        public void notifyError(@TvInputManager.RecordingError final int error) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifyError");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onError(error);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyError", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Dispatches an event to the application using this recording session.
+         *
+         * @param eventType The type of the event.
+         * @param eventArgs Optional arguments of the event.
+         * @hide
+         */
+        @SystemApi
+        public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) {
+            Preconditions.checkNotNull(eventType);
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onSessionEvent(eventType, eventArgs);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in sending event (event=" + eventType + ")", e);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Called when the recording session is connected.
+         *
+         * @param channelUri The URI of the channel.
+         */
+        public abstract void onConnect(Uri channelUri);
+
+        /**
+         * Called when the recording session is connected.
+         *
+         * @param channelUri The URI of the channel.
+         * @param params Extra parameters.
+         * @hide
+         */
+        @SystemApi
+        public void onConnect(Uri channelUri, Bundle params) {
+            onConnect(channelUri);
+        }
+
+        /**
+         * Called when the application requests to disconnect the current recording session.
+         */
+        public abstract void onDisconnect();
+
+        /**
+         * Called when the application requests to start recording. Recording must start
+         * immediately.
+         *
+         * <p>The session must call either {@link #notifyRecordingStarted()} or
+         * {@link #notifyError(int)}}.
+         */
+        public abstract void onStartRecording();
+
+        /**
+         * Called when the application requests to stop recording. Recording must stop immediately.
+         *
+         * <p>The session must call either {@link #notifyRecordingStopped(Uri)} or
+         * {@link #notifyError(int)}}.
+         */
+        public abstract void onStopRecording();
+
+        /**
+         * Processes a private command sent from the application to the TV input. This can be used
+         * to provide domain-specific features that are only known between certain TV inputs and
+         * their clients.
+         *
+         * @param action Name of the command to be performed. This <em>must</em> be a scoped name,
+         *            i.e. prefixed with a package name you own, so that different developers will
+         *            not create conflicting commands.
+         * @param data Any data to include with the command.
+         * @hide
+         */
+        @SystemApi
+        public void onAppPrivateCommand(@NonNull String action, Bundle data) {
+        }
+
+        /**
+         * Calls {@link #onConnect(Uri, Bundle)}.
+         *
+         */
+        void connect(Uri channelUri, Bundle params) {
+            onConnect(channelUri, params);
+        }
+
+        /**
+         * Calls {@link #onDisconnect()}.
+         *
+         */
+        void disconnect() {
+            onDisconnect();
+        }
+
+        /**
+         * Calls {@link #onStartRecording()}.
+         *
+         */
+        void startRecording() {
+            onStartRecording();
+        }
+
+        /**
+         * Calls {@link #onStopRecording()}.
+         *
+         */
+        void stopRecording() {
+            onStopRecording();
+        }
+
+        /**
+         * Calls {@link #onAppPrivateCommand(String, Bundle)}.
+         */
+        void appPrivateCommand(String action, Bundle data) {
+            onAppPrivateCommand(action, data);
+        }
+
+        private void initialize(ITvInputSessionCallback callback) {
+            synchronized(mLock) {
+                mSessionCallback = callback;
+                for (Runnable runnable : mPendingActions) {
+                    runnable.run();
+                }
+                mPendingActions.clear();
+            }
+        }
+
+        private void executeOrPostRunnableOnMainThread(Runnable action) {
+            synchronized(mLock) {
+                if (mSessionCallback == null) {
+                    // The session is not initialized yet.
+                    mPendingActions.add(action);
+                } else {
+                    if (mHandler.getLooper().isCurrentThread()) {
+                        action.run();
+                    } else {
+                        // Posts the runnable if this is not called from the main thread
+                        mHandler.post(action);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Base class for a TV input session which represents an external device connected to a
      * hardware TV input.
      *
@@ -1588,10 +1933,12 @@
     private final class ServiceHandler extends Handler {
         private static final int DO_CREATE_SESSION = 1;
         private static final int DO_NOTIFY_SESSION_CREATED = 2;
-        private static final int DO_ADD_HARDWARE_TV_INPUT = 3;
-        private static final int DO_REMOVE_HARDWARE_TV_INPUT = 4;
-        private static final int DO_ADD_HDMI_TV_INPUT = 5;
-        private static final int DO_REMOVE_HDMI_TV_INPUT = 6;
+        private static final int DO_CREATE_RECORDING_SESSION = 3;
+        private static final int DO_ADD_HARDWARE_TV_INPUT = 4;
+        private static final int DO_REMOVE_HARDWARE_TV_INPUT = 5;
+        private static final int DO_ADD_HDMI_TV_INPUT = 6;
+        private static final int DO_REMOVE_HDMI_TV_INPUT = 7;
+        private static final int DO_SET_TV_INPUT_INFO = 8;
 
         private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
             int n = mCallbacks.beginBroadcast();
@@ -1629,6 +1976,18 @@
             mCallbacks.finishBroadcast();
         }
 
+        private void broadcastSetTvInputInfo(String inputId, TvInputInfo inputInfo) {
+            int n = mCallbacks.beginBroadcast();
+            for (int i = 0; i < n; ++i) {
+                try {
+                    mCallbacks.getBroadcastItem(i).setTvInputInfo(inputId, inputInfo);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "error in broadcastSetTvInputInfo", e);
+                }
+            }
+            mCallbacks.finishBroadcast();
+        }
+
         @Override
         public final void handleMessage(Message msg) {
             switch (msg.what) {
@@ -1704,6 +2063,31 @@
                     args.recycle();
                     return;
                 }
+                case DO_CREATE_RECORDING_SESSION: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg1;
+                    String inputId = (String) args.arg2;
+                    args.recycle();
+                    RecordingSession recordingSessionImpl = onCreateRecordingSession(inputId);
+                    if (recordingSessionImpl == null) {
+                        try {
+                            // Failed to create a recording session.
+                            cb.onSessionCreated(null, null);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "error in onSessionCreated", e);
+                        }
+                        return;
+                    }
+                    ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
+                            recordingSessionImpl);
+                    try {
+                        cb.onSessionCreated(stub, null);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "error in onSessionCreated", e);
+                    }
+                    recordingSessionImpl.initialize(cb);
+                    return;
+                }
                 case DO_ADD_HARDWARE_TV_INPUT: {
                     TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj;
                     TvInputInfo inputInfo = onHardwareAdded(hardwareInfo);
@@ -1736,6 +2120,16 @@
                     }
                     return;
                 }
+                case DO_SET_TV_INPUT_INFO: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String inputId = (String) args.arg1;
+                    TvInputInfo inputInfo = (TvInputInfo) args.arg2;
+                    if (inputInfo != null) {
+                        broadcastSetTvInputInfo(inputId, inputInfo);
+                    }
+                    args.recycle();
+                    return;
+                }
                 default: {
                     Log.w(TAG, "Unhandled message code: " + msg.what);
                     return;
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
new file mode 100644
index 0000000..865e000
--- /dev/null
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * The public interface object used to interact with a specific TV input service for TV program
+ * recording.
+ */
+public class TvRecordingClient {
+    private static final String TAG = "TvRecordingClient";
+    private static final boolean DEBUG = false;
+
+    private final RecordingCallback mCallback;
+    private final Handler mHandler;
+
+    private final TvInputManager mTvInputManager;
+    private TvInputManager.Session mSession;
+    private MySessionCallback mSessionCallback;
+
+    private final Queue<Pair<String, Bundle>> mPendingAppPrivateCommands = new ArrayDeque<>();
+
+    /**
+     * Creates a new TvRecordingClient object.
+     *
+     * @param context The application context to create the TvRecordingClient with.
+     * @param tag A short name for debugging purposes.
+     * @param callback The callback to receive recording status changes.
+     * @param handler The handler to invoke the callback on.
+     */
+    public TvRecordingClient(Context context, String tag, @NonNull RecordingCallback callback,
+            Handler handler) {
+        mCallback = callback;
+        mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
+        mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
+    }
+
+    /**
+     * Connects to a given input for TV program recording. This will create a new recording session
+     * from the TV input and establishes the connection between the application and the session.
+     *
+     * <p>The recording session will respond by calling
+     * {@link RecordingCallback#onConnected()} or {@link RecordingCallback#onError(int)}.
+     *
+     * @param inputId The ID of the TV input for the given channel.
+     * @param channelUri The URI of a channel.
+     */
+    public void connect(String inputId, Uri channelUri) {
+        connect(inputId, channelUri, null);
+    }
+
+    /**
+     * Connects to a given input for TV program recording. This will create a new recording session
+     * from the TV input and establishes the connection between the application and the session.
+     *
+     * <p>The recording session will respond by calling
+     * {@link RecordingCallback#onConnected()} or {@link RecordingCallback#onError(int)}.
+     *
+     * @param inputId The ID of the TV input for the given channel.
+     * @param channelUri The URI of a channel.
+     * @param params Extra parameters.
+     * @hide
+     */
+    @SystemApi
+    public void connect(String inputId, Uri channelUri, Bundle params) {
+        if (DEBUG) Log.d(TAG, "connect(" + channelUri + ")");
+        if (TextUtils.isEmpty(inputId)) {
+            throw new IllegalArgumentException("inputId cannot be null or an empty string");
+        }
+        if (mSessionCallback != null && TextUtils.equals(mSessionCallback.mInputId, inputId)) {
+            if (mSession != null) {
+                mSession.connect(channelUri, params);
+            } else {
+                mSessionCallback.mChannelUri = channelUri;
+                mSessionCallback.mConnectionParams = params;
+            }
+        } else {
+            resetInternal();
+            mSessionCallback = new MySessionCallback(inputId, channelUri, params);
+            if (mTvInputManager != null) {
+                mTvInputManager.createRecordingSession(inputId, mSessionCallback, mHandler);
+            }
+        }
+    }
+
+    /**
+     * Disconnects the established connection between the application and the recording session.
+     *
+     * <p>The recording session will respond by calling
+     * {@link RecordingCallback#onDisconnected()} or {@link RecordingCallback#onError(int)}.
+     */
+    public void disconnect() {
+        if (DEBUG) Log.d(TAG, "disconnect()");
+        resetInternal();
+    }
+
+    private void resetInternal() {
+        mSessionCallback = null;
+        mPendingAppPrivateCommands.clear();
+        if (mSession != null) {
+            mSession.release();
+            mSession = null;
+        }
+    }
+
+    /**
+     * Starts TV program recording for the current recording session. It is expected that recording
+     * starts immediately after calling this method.
+     *
+     * <p>The recording session will respond by calling
+     * {@link RecordingCallback#onRecordingStarted()} or {@link RecordingCallback#onError(int)}.
+     */
+    public void startRecording() {
+        if (mSession != null) {
+            mSession.startRecording();
+        }
+    }
+
+    /**
+     * Stops TV program recording for the current recording session. It is expected that recording
+     * stops immediately after calling this method.
+     *
+     * <p>The recording session will respond by calling
+     * {@link RecordingCallback#onRecordingStopped(Uri)} or {@link RecordingCallback#onError(int)}.
+     */
+    public void stopRecording() {
+        if (mSession != null) {
+            mSession.stopRecording();
+        }
+    }
+
+    /**
+     * Calls {@link TvInputService.RecordingSession#appPrivateCommand(String, Bundle)
+     * TvInputService.RecordingSession.appPrivateCommand()} on the current TvView.
+     *
+     * @param action The name of the private command to send. This <em>must</em> be a scoped name,
+     *            i.e. prefixed with a package name you own, so that different developers will not
+     *            create conflicting commands.
+     * @param data An optional bundle to send with the command.
+     * @hide
+     */
+    @SystemApi
+    public void sendAppPrivateCommand(@NonNull String action, Bundle data) {
+        if (TextUtils.isEmpty(action)) {
+            throw new IllegalArgumentException("action cannot be null or an empty string");
+        }
+        if (mSession != null) {
+            mSession.sendAppPrivateCommand(action, data);
+        } else {
+            Log.w(TAG, "sendAppPrivateCommand - session not yet created (action \"" + action
+                    + "\" pending)");
+            mPendingAppPrivateCommands.add(Pair.create(action, data));
+        }
+    }
+
+    /**
+     * Callback used to receive various status updates on the
+     * {@link android.media.tv.TvInputService.RecordingSession}
+     */
+    public class RecordingCallback {
+        /**
+         * This is called when a recording session initiated by a call to
+         * {@link #connect(String, Uri)} has been established.
+         */
+        public void onConnected() {
+        }
+
+        /**
+         * This is called when the established connection between the application and the recording
+         * session has been disconnected. Disconnection can be initiated either by an explicit
+         * request (i.e. a call to {@link #disconnect()} or by an error on the TV input service
+         * side.
+         */
+        public void onDisconnected() {
+        }
+
+        /**
+         * This is called when TV program recording on the current channel has started.
+         */
+        public void onRecordingStarted() {
+        }
+
+        /**
+         * This is called when TV program recording on the current channel has stopped. The passed
+         * URI contains information about the new recorded program.
+         *
+         * @param recordedProgramUri The URI for the new recorded program.
+         * @see android.media.tv.TvContract.RecordedPrograms
+         */
+        public void onRecordingStopped(Uri recordedProgramUri) {
+        }
+
+        /**
+         * This is called when an issue has occurred before or during recording. If the TV input
+         * service cannot proceed recording due to this error, a call to {@link #onDisconnected()}
+         * is expected to follow.
+         *
+         * @param error The error code. Should be one of the followings.
+         * <ul>
+         * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
+         * <li>{@link TvInputManager#RECORDING_ERROR_CONNECTION_FAILED}
+         * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
+         * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
+         * </ul>
+         */
+        public void onError(@TvInputManager.RecordingError int error) {
+        }
+
+        /**
+         * This is invoked when a custom event from the bound TV input is sent to this client.
+         *
+         * @param inputId The ID of the TV input bound to this client.
+         * @param eventType The type of the event.
+         * @param eventArgs Optional arguments of the event.
+         * @hide
+         */
+        @SystemApi
+        public void onEvent(String inputId, String eventType, Bundle eventArgs) {
+        }
+    }
+
+    private class MySessionCallback extends TvInputManager.SessionCallback {
+        final String mInputId;
+        Uri mChannelUri;
+        Bundle mConnectionParams;
+
+        MySessionCallback(String inputId, Uri channelUri, Bundle connectionParams) {
+            mInputId = inputId;
+            mChannelUri = channelUri;
+            mConnectionParams = connectionParams;
+        }
+
+        @Override
+        public void onSessionCreated(TvInputManager.Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionCreated()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSessionCreated - session already created");
+                // This callback is obsolete.
+                if (session != null) {
+                    session.release();
+                }
+                return;
+            }
+            mSession = session;
+            if (session != null) {
+                // Sends the pending app private commands.
+                for (Pair<String, Bundle> command : mPendingAppPrivateCommands) {
+                    mSession.sendAppPrivateCommand(command.first, command.second);
+                }
+                mPendingAppPrivateCommands.clear();
+                mSession.connect(mChannelUri, mConnectionParams);
+            } else {
+                mSessionCallback = null;
+                mCallback.onError(TvInputManager.RECORDING_ERROR_CONNECTION_FAILED);
+            }
+        }
+
+        @Override
+        public void onSessionReleased(TvInputManager.Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionReleased()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSessionReleased - session not created");
+                return;
+            }
+            mSessionCallback = null;
+            mSession = null;
+            mCallback.onDisconnected();
+        }
+
+        @Override
+        public void onRecordingStarted(TvInputManager.Session session) {
+            if (DEBUG) {
+                Log.d(TAG, "onRecordingStarted()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRecordingStarted - session not created");
+                return;
+            }
+            mCallback.onRecordingStarted();
+        }
+
+        @Override
+        public void onRecordingStopped(TvInputManager.Session session, Uri recordedProgramUri) {
+            if (DEBUG) {
+                Log.d(TAG, "onRecordingStopped()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onRecordingStopped - session not created");
+                return;
+            }
+            mCallback.onRecordingStopped(recordedProgramUri);
+        }
+
+        @Override
+        public void onError(TvInputManager.Session session, int error) {
+            if (DEBUG) {
+                Log.d(TAG, "onError()");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onError - session not created");
+                return;
+            }
+            mCallback.onError(error);
+        }
+
+        @Override
+        public void onSessionEvent(TvInputManager.Session session, String eventType,
+                Bundle eventArgs) {
+            if (DEBUG) {
+                Log.d(TAG, "onSessionEvent(" + eventType + ")");
+            }
+            if (this != mSessionCallback) {
+                Log.w(TAG, "onSessionEvent - session not created");
+                return;
+            }
+            if (mCallback != null) {
+                mCallback.onEvent(mInputId, eventType, eventArgs);
+            }
+        }
+    }
+}
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 003a274..0132d24 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -448,6 +448,37 @@
     }
 
     /**
+     * Plays a given recorded TV program.
+     *
+     * @param inputId The ID of the TV input that created the given recorded program.
+     * @param recordedProgramUri The URI of a recorded program.
+     */
+    public void timeShiftPlay(String inputId, Uri recordedProgramUri) {
+        if (DEBUG) Log.d(TAG, "timeShiftPlay(" + recordedProgramUri + ")");
+        if (TextUtils.isEmpty(inputId)) {
+            throw new IllegalArgumentException("inputId cannot be null or an empty string");
+        }
+        synchronized (sMainTvViewLock) {
+            if (sMainTvView.get() == null) {
+                sMainTvView = new WeakReference<>(this);
+            }
+        }
+        if (mSessionCallback != null && TextUtils.equals(mSessionCallback.mInputId, inputId)) {
+            if (mSession != null) {
+                mSession.timeShiftPlay(recordedProgramUri);
+            } else {
+                mSessionCallback.mRecordedProgramUri = recordedProgramUri;
+            }
+        } else {
+            resetInternal();
+            mSessionCallback = new MySessionCallback(inputId, recordedProgramUri);
+            if (mTvInputManager != null) {
+                mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
+            }
+        }
+    }
+
+    /**
      * Pauses playback. No-op if it is already paused. Call {@link #timeShiftResume} to resume.
      */
     public void timeShiftPause() {
@@ -994,6 +1025,7 @@
         final String mInputId;
         Uri mChannelUri;
         Bundle mTuneParams;
+        Uri mRecordedProgramUri;
 
         MySessionCallback(String inputId, Uri channelUri, Bundle tuneParams) {
             mInputId = inputId;
@@ -1001,6 +1033,11 @@
             mTuneParams = tuneParams;
         }
 
+        MySessionCallback(String inputId, Uri recordedProgramUri) {
+            mInputId = inputId;
+            mRecordedProgramUri = recordedProgramUri;
+        }
+
         @Override
         public void onSessionCreated(Session session) {
             if (DEBUG) {
@@ -1043,7 +1080,11 @@
                 if (mCaptionEnabled != null) {
                     mSession.setCaptionEnabled(mCaptionEnabled);
                 }
-                mSession.tune(mChannelUri, mTuneParams);
+                if (mChannelUri != null) {
+                    mSession.tune(mChannelUri, mTuneParams);
+                } else {
+                    mSession.timeShiftPlay(mRecordedProgramUri);
+                }
                 ensurePositionTracking();
             } else {
                 mSessionCallback = null;
diff --git a/packages/DocumentsUI/res/layout/item_dir_grid.xml b/packages/DocumentsUI/res/layout/item_dir_grid.xml
index a08e029..b0331be 100644
--- a/packages/DocumentsUI/res/layout/item_dir_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_dir_grid.xml
@@ -17,7 +17,7 @@
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_height="wrap_content"
     android:layout_margin="@dimen/grid_item_margin"
     android:background="@color/item_doc_background"
     android:elevation="5dp"
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 8ac228e..5a9bc16 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -179,8 +179,8 @@
     </plurals>
     <!-- Second line for notifications saying that more information will be shown after touching [CHAR LIMIT=48] -->
     <string name="notification_touch_for_details">Tap to view details</string>
-    <!-- Label of a dialog button for retrying a failed operation [CHAR LIMIT=24] -->
-    <string name="retry">Retry</string>
+    <!-- Label of the close dialog button.[CHAR LIMIT=24] -->
+    <string name="close">Close</string>
     <!-- Contents of the copying failure alert dialog. [CHAR LIMIT=48] -->
     <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string>
     <!-- Contents of the moving failure alert dialog. [CHAR LIMIT=48] -->
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index f43b81c..ad8b0d1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -107,6 +107,8 @@
                 ? icicle.<State>getParcelable(EXTRA_STATE)
                         : buildState();
 
+        Metrics.logActivityLaunch(this, mState, getIntent());
+
         setContentView(mLayoutId);
 
         mRoots = DocumentsApplication.getRootsCache(this);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index 7f6f1c6..b8ef5ac 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -33,17 +33,14 @@
 import com.android.documentsui.services.FileOperations;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Alert dialog for failed operations.
  */
-public class FailureDialogFragment extends DialogFragment
-        implements DialogInterface.OnClickListener {
+public class FailureDialogFragment extends DialogFragment {
     private static final String TAG = "FailureDialogFragment";
 
-    private int mOperationType;
-    private ArrayList<DocumentInfo> mFailedSrcList;
-
     public static void show(FragmentManager fm, int failure,
             ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack, int operationType) {
         // TODO: Add support for other failures than copy.
@@ -65,36 +62,25 @@
     }
 
     @Override
-    public void onClick(DialogInterface dialog, int whichButton) {
-        if (whichButton == DialogInterface.BUTTON_POSITIVE) {
-            FileOperations.start(
-                    getActivity(),
-                    mFailedSrcList,
-                    (DocumentStack) getActivity().getIntent().getParcelableExtra(
-                            Shared.EXTRA_STACK),
-                    mOperationType);
-        }
-    }
-
-    @Override
     public Dialog onCreateDialog(Bundle inState) {
         super.onCreate(inState);
 
-        mOperationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION);
-        mFailedSrcList = getArguments().getParcelableArrayList(FileOperationService.EXTRA_SRC_LIST);
+        final int operationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION);
+        final List<DocumentInfo> failedSrcList = getArguments().getParcelableArrayList(
+                FileOperationService.EXTRA_SRC_LIST);
 
         final StringBuilder list = new StringBuilder("<p>");
-        for (DocumentInfo documentInfo : mFailedSrcList) {
+        for (DocumentInfo documentInfo : failedSrcList) {
             list.append(String.format("&#8226; %s<br>", documentInfo.displayName));
         }
         list.append("</p>");
 
         // TODO: Add support for other file operations.
         checkArgument(
-                mOperationType == FileOperationService.OPERATION_COPY
-                || mOperationType == FileOperationService.OPERATION_MOVE);
+                operationType == FileOperationService.OPERATION_COPY
+                || operationType == FileOperationService.OPERATION_MOVE);
 
-        int messageId = mOperationType == FileOperationService.OPERATION_COPY
+        int messageId = operationType == FileOperationService.OPERATION_COPY
                 ? R.string.copy_failure_alert_content
                 : R.string.move_failure_alert_content;
 
@@ -105,8 +91,12 @@
 
         return new AlertDialog.Builder(getActivity())
                 .setMessage(Html.fromHtml(message))
-                .setPositiveButton(R.string.retry, this)
-                .setNegativeButton(android.R.string.cancel, this)
+                .setPositiveButton(R.string.close, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                        dialog.dismiss();
+                    }
+                })
                 .create();
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 58537ee..5abe7f6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -282,6 +282,7 @@
     }
 
     private void createNewWindow() {
+        Metrics.logMultiWindow(this);
         Intent intent = LauncherActivity.createLaunchIntent(this);
         intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
         startActivity(intent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
new file mode 100644
index 0000000..eb90b75
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.Shared.DEBUG;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import com.android.documentsui.model.RootInfo;
+import com.android.internal.logging.MetricsLogger;
+
+/** @hide */
+public final class Metrics {
+    private static final String TAG = "Metrics";
+
+    // These are the native provider authorities that the metrics code is capable of recognizing and
+    // explicitly counting.
+    private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
+    private static final String AUTHORITY_STORAGE = "com.android.externalstorage.documents";
+    private static final String AUTHORITY_DOWNLOADS = "com.android.providers.downloads.documents";
+
+    // These strings have to be whitelisted in tron. Do not change them.
+    private static final String COUNT_LAUNCH_ACTION = "docsui_launch_action";
+    private static final String COUNT_ROOT_VISITED = "docsui_root_visited";
+    private static final String COUNT_OPEN_MIME = "docsui_open_mime";
+    private static final String COUNT_CREATE_MIME = "docsui_create_mime";
+    private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
+    private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
+    private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
+    private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
+
+    // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
+    // root that is not explicitly recognized by the Metrics code (see {@link
+    // #getSanitizedRootIndex}). Apps are also bucketed in this histogram using negative indices
+    // (see below).
+    private static final int ROOT_NONE = 0;
+    private static final int ROOT_OTHER = 1;
+    private static final int ROOT_AUDIO = 2;
+    private static final int ROOT_DEVICE_STORAGE = 3;
+    private static final int ROOT_DOWNLOADS = 4;
+    private static final int ROOT_HOME = 5;
+    private static final int ROOT_IMAGES = 6;
+    private static final int ROOT_RECENTS = 7;
+    private static final int ROOT_VIDEOS = 8;
+    // Apps aren't really "roots", but they are treated as such in the roots fragment UI and so they
+    // are logged analogously to roots. Use negative numbers to identify apps.
+    private static final int ROOT_THIRD_PARTY_APP = -1;
+
+    // Indices for bucketing mime types.
+    private static final int MIME_OTHER = -2; // anything not enumerated below
+    private static final int MIME_NONE = -1; // null mime
+    private static final int MIME_ANY = 0; // */*
+    private static final int MIME_APPLICATION = 1; // application/*
+    private static final int MIME_AUDIO = 2; // audio/*
+    private static final int MIME_IMAGE = 3; // image/*
+    private static final int MIME_MESSAGE = 4; // message/*
+    private static final int MIME_MULTIPART = 5; // multipart/*
+    private static final int MIME_TEXT = 6; // text/*
+    private static final int MIME_VIDEO = 7; // video/*
+
+    /**
+     * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
+     *
+     * @param context
+     * @param state
+     * @param intent
+     */
+    public static void logActivityLaunch(Context context, State state, Intent intent) {
+        // Log the launch action.
+        logHistogram(context, COUNT_LAUNCH_ACTION, state.action);
+        // Then log auxiliary data (roots/mime types) associated with some actions.
+        Uri uri = intent.getData();
+        switch (state.action) {
+            case State.ACTION_OPEN:
+                logHistogram(context, COUNT_OPEN_MIME, sanitizeMime(intent.getType()));
+                break;
+            case State.ACTION_CREATE:
+                logHistogram(context, COUNT_CREATE_MIME, sanitizeMime(intent.getType()));
+                break;
+            case State.ACTION_GET_CONTENT:
+                logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType()));
+                break;
+            case State.ACTION_MANAGE:
+                logHistogram(context, COUNT_MANAGE_ROOT, sanitizeRoot(uri));
+                break;
+            case State.ACTION_BROWSE:
+                logHistogram(context, COUNT_BROWSE_ROOT, sanitizeRoot(uri));
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Logs a root visited event. Call this when the user clicks on a root in the RootsFragment.
+     *
+     * @param context
+     * @param info
+     */
+    public static void logRootVisited(Context context, RootInfo info) {
+        logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
+    }
+
+    /**
+     * Logs an app visited event. Call this when the user clicks on an app in the RootsFragment.
+     *
+     * @param context
+     * @param info
+     */
+    public static void logAppVisited(Context context, ResolveInfo info) {
+        logHistogram(context, COUNT_ROOT_VISITED, sanitizeRoot(info));
+    }
+
+    /**
+     * Logs a multi-window start. Call this when the user spawns a new DocumentsUI window.
+     *
+     * @param context
+     */
+    public static void logMultiWindow(Context context) {
+        logCount(context, COUNT_MULTI_WINDOW);
+    }
+
+    /**
+     * Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
+     *
+     * @param context
+     * @param name The counter to increment.
+     */
+    private static void logCount(Context context, String name) {
+        if (DEBUG) Log.d(TAG, name + ": " + 1);
+        MetricsLogger.count(context, name, 1);
+    }
+
+    /**
+     * Internal method for making a MetricsLogger.histogram call.
+     *
+     * @param context
+     * @param name The name of the histogram.
+     * @param bucket The bucket to increment.
+     */
+    private static void logHistogram(Context context, String name, int bucket) {
+        if (DEBUG) Log.d(TAG, name + ": " + bucket);
+        MetricsLogger.histogram(context, name, bucket);
+    }
+
+    /**
+     * Generates an integer identifying the given root. For privacy, this function only recognizes a
+     * small set of hard-coded roots (ones provided by the system). Other roots are all grouped into
+     * a single ROOT_OTHER bucket.
+     */
+    private static int sanitizeRoot(Uri uri) {
+        if (LauncherActivity.isLaunchUri(uri)) {
+            return ROOT_NONE;
+        }
+
+        switch (uri.getAuthority()) {
+            case AUTHORITY_MEDIA:
+                switch (DocumentsContract.getRootId(uri)) {
+                    case "audio_root":
+                        return ROOT_AUDIO;
+                    case "images_root":
+                        return ROOT_IMAGES;
+                    case "videos_root":
+                        return ROOT_VIDEOS;
+                    default:
+                        return ROOT_OTHER;
+                }
+            case AUTHORITY_STORAGE:
+                if ("home".equals(DocumentsContract.getRootId(uri))) {
+                    return ROOT_HOME;
+                } else {
+                    return ROOT_DEVICE_STORAGE;
+                }
+            case AUTHORITY_DOWNLOADS:
+                return ROOT_DOWNLOADS;
+            default:
+                return ROOT_OTHER;
+        }
+    }
+
+    /** @see #sanitizeRoot(Uri) */
+    private static int sanitizeRoot(RootInfo root) {
+        if (root.isRecents()) {
+            // Recents root is special and only identifiable via this method call. Other roots are
+            // identified by URI.
+            return ROOT_RECENTS;
+        } else {
+            return sanitizeRoot(root.getUri());
+        }
+    }
+
+    /** @see #sanitizeRoot(Uri) */
+    private static int sanitizeRoot(ResolveInfo info) {
+        // Log all apps under a single bucket in the roots histogram.
+        return ROOT_THIRD_PARTY_APP;
+    }
+
+    /**
+     * Generates an int identifying a mime type. For privacy, this function only recognizes a small
+     * set of hard-coded types. For any other type, this function returns "other".
+     *
+     * @param mimeType
+     * @return
+     */
+    private static int sanitizeMime(String mimeType) {
+        if (mimeType == null) {
+            return MIME_NONE;
+        } else if ("*/*".equals(mimeType)) {
+            return MIME_ANY;
+        } else {
+            String type = mimeType.substring(0, mimeType.indexOf('/'));
+            switch (type) {
+                case "application":
+                    return MIME_APPLICATION;
+                case "audio":
+                    return MIME_AUDIO;
+                case "image":
+                    return MIME_IMAGE;
+                case "message":
+                    return MIME_MESSAGE;
+                case "multipart":
+                    return MIME_MULTIPART;
+                case "text":
+                    return MIME_TEXT;
+                case "video":
+                    return MIME_VIDEO;
+            }
+        }
+        // Bucket all other types into one bucket.
+        return MIME_OTHER;
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index beff196..c2aeb86 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -180,10 +180,14 @@
             Item item = mAdapter.getItem(position);
             if (item instanceof RootItem) {
                 BaseActivity activity = BaseActivity.get(RootsFragment.this);
-                activity.onRootPicked(((RootItem) item).root);
+                RootInfo info = ((RootItem) item).root;
+                Metrics.logRootVisited(getActivity(), info);
+                activity.onRootPicked(info);
             } else if (item instanceof AppItem) {
                 DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
-                activity.onAppPicked(((AppItem) item).info);
+                ResolveInfo info = ((AppItem) item).info;
+                Metrics.logAppVisited(getActivity(), info);
+                activity.onAppPicked(info);
             } else if (item instanceof SpacerItem) {
                 if (DEBUG) Log.d(TAG, "Ignoring click on spacer item.");
             } else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
index 7a238bd..2a0262c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
@@ -21,6 +21,7 @@
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.content.Context;
+import android.net.Uri;
 import android.os.RemoteException;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
@@ -85,7 +86,7 @@
         if (src.authority.equals(dest.authority)) {
             if ((src.flags & Document.FLAG_SUPPORTS_MOVE) != 0) {
                 if (DocumentsContract.moveDocument(getClient(src), src.derivedUri,
-                        dest.derivedUri) == null) {
+                        Uri.EMPTY /* Not used yet */, dest.derivedUri) == null) {
                     onFileFailed(src);
                     return false;
                 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
index 3c40b67..467d97e 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
@@ -20,6 +20,7 @@
 import static android.provider.DocumentsContract.buildDocumentUri;
 import static android.provider.DocumentsContract.buildRootsUri;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
+import static com.android.internal.util.Preconditions.checkArgument;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.fail;
@@ -37,7 +38,7 @@
 import android.provider.DocumentsContract.Root;
 import android.support.annotation.Nullable;
 import android.test.MoreAsserts;
-import android.util.Log;
+import android.text.TextUtils;
 
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.RootInfo;
@@ -60,13 +61,13 @@
     private final ContentProviderClient mClient;
 
     public DocumentsProviderHelper(String authority, ContentProviderClient client) {
+        checkArgument(!TextUtils.isEmpty(authority));
         mAuthority = authority;
         mClient = client;
     }
 
     public RootInfo getRoot(String documentId) throws RemoteException {
         final Uri rootsUri = buildRootsUri(mAuthority);
-
         Cursor cursor = null;
         try {
             cursor = mClient.query(rootsUri, null, null, null, null);
@@ -126,7 +127,6 @@
     }
 
     public byte[] readDocument(Uri documentUri) throws RemoteException, IOException {
-        Log.d("DocumentsProviderHelper", "Trying to read file contents: " + documentUri);
         ParcelFileDescriptor file = mClient.openFile(documentUri, "r", null);
         byte[] buf = null;
         try (AutoCloseInputStream in = new AutoCloseInputStream(file)) {
@@ -245,21 +245,18 @@
         Uri uri = buildChildDocumentsUri(mAuthority, documentId);
         List<DocumentInfo> children = new ArrayList<>();
         try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) {
-            while (cursor.moveToNext()) {
-                children.add(DocumentInfo.fromDirectoryCursor(cursor));
+            Cursor wrapper = new RootCursorWrapper(mAuthority, "totally-fake", cursor, 100);
+            while (wrapper.moveToNext()) {
+                children.add(DocumentInfo.fromDirectoryCursor(wrapper));
             }
         }
         return children;
     }
 
     public void assertFileContents(Uri documentUri, byte[] expected) throws Exception {
-        // TODO: Fix this: java.lang.SecurityException:
-        // The authority of the uri content:/document/%2Fdata%2Fuser%2F0%2Fcom.android.documentsui.\
-        // tests%2Fcache%2FTEST_ROOT_1%2Ftest1.txt does not match the one of the contentProvider: \
-        // com.android.documentsui.stubprovider
-//        MoreAsserts.assertEquals(
-//                "Copied file contents differ",
-//                expected, readDocument(documentUri));
+        MoreAsserts.assertEquals(
+                "Copied file contents differ",
+                expected, readDocument(documentUri));
     }
 
     public void assertFileContents(String parentId, String fileName, byte[] expected)
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index c6e5531..43527a2 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -463,7 +463,8 @@
     }
 
     @Override
-    public String moveDocument(String sourceDocumentId, String targetParentDocumentId)
+    public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
+            String targetParentDocumentId)
             throws FileNotFoundException {
         final File before = getFileForDocId(sourceDocumentId);
         final File after = new File(getFileForDocId(targetParentDocumentId), before.getName());
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index 66a3247..7b57d89 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -332,23 +332,20 @@
             uint64_t offset,
             uint32_t size,
             void* buf) {
-        const uint32_t read_size = static_cast<uint32_t>(std::min(
-                static_cast<uint64_t>(size),
-                get_file_size(inode) - offset));
         ScopedLocalRef<jbyteArray> array(env_, static_cast<jbyteArray>(env_->CallObjectMethod(
                 self_,
                 app_fuse_get_object_bytes,
                 inode,
                 offset,
-                read_size)));
+                size)));
         if (array.get() == nullptr) {
             return -1;
         }
         ScopedByteArrayRO bytes(env_, array.get());
-        if (bytes.size() != read_size || bytes.get() == nullptr) {
+        if (bytes.get() == nullptr) {
             return -1;
         }
-
+        const uint32_t read_size = std::min(static_cast<uint32_t>(bytes.size()), size);
         memcpy(buf, bytes.get(), read_size);
         return read_size;
     }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 3381656..3ac1b3d 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -37,10 +37,10 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -103,18 +103,28 @@
     }
 
     @VisibleForTesting
-    void onCreateForTesting(
+    boolean onCreateForTesting(
             Resources resources,
             MtpManager mtpManager,
             ContentResolver resolver,
-            MtpDatabase database) {
+            MtpDatabase database,
+            StorageManager storageManager) {
         mResources = resources;
         mMtpManager = mtpManager;
         mResolver = resolver;
         mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
         mDatabase = database;
         mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
+        mAppFuse = new AppFuse(TAG, new AppFuseCallback());
+        // TODO: Mount AppFuse on demands.
+        try {
+            mAppFuse.mount(storageManager);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to start app fuse.", e);
+            return false;
+        }
         resume();
+        return true;
     }
 
     @Override
@@ -165,7 +175,7 @@
                     // Fallback to non-seekable file descriptor.
                     // TODO: Use getPartialObject64 for MTP devices that support Android vendor
                     // extension.
-                    if (fileSize <= 0xffffffff) {
+                    if (fileSize <= 0xffffffffl) {
                         return mAppFuse.openFile(Integer.parseInt(documentId));
                     } else {
                         return getPipeManager(identifier).readDocument(mMtpManager, identifier);
@@ -305,6 +315,7 @@
                 throw new RuntimeException(e);
             } finally {
                 mDatabase.close();
+                mAppFuse.close();
                 super.shutdown();
             }
         }
@@ -385,9 +396,11 @@
         @Override
         public byte[] getObjectBytes(int inode, long offset, int size) throws IOException {
             final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode));
-            mMtpManager.getPartialObject(
-                    identifier.mDeviceId, identifier.mObjectHandle, (int) offset, size, mBytes);
-            return mBytes;
+            final long readSize = mMtpManager.getPartialObject(
+                    identifier.mDeviceId, identifier.mObjectHandle, offset, size, mBytes);
+            // TODO: Change signature so that getObjectBytes can return read size without copying
+            // bytes.
+            return Arrays.copyOf(mBytes, (int) readSize);
         }
 
         @Override
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index b51cf71..7066f7d 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -20,18 +20,25 @@
 import android.mtp.MtpConstants;
 import android.mtp.MtpObjectInfo;
 import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
 import android.provider.DocumentsContract.Root;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.provider.DocumentsContract;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.concurrent.TimeoutException;
 
 import static com.android.mtp.MtpDatabase.strings;
 
-@SmallTest
+@MediumTest
 public class MtpDocumentsProviderTest extends AndroidTestCase {
     private final static Uri ROOTS_URI =
             DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
@@ -421,10 +428,71 @@
                         MtpDocumentsProvider.AUTHORITY, "1")));
     }
 
+    public void testOpenDocument() throws Exception {
+        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+        setupRoots(0, new MtpRoot[] {
+                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
+        });
+        final byte[] bytes = "Hello world".getBytes();
+        setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
+                new MtpObjectInfo.Builder()
+                        .setName("test.txt")
+                        .setObjectHandle(1)
+                        .setCompressedSize(bytes.length)
+                        .setParent(-1)
+                        .build()
+        });
+        mMtpManager.setImportFileBytes(0, 1, bytes);
+        try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) {
+            final byte[] readBytes = new byte[5];
+            assertEquals(6, Os.lseek(fd.getFileDescriptor(), 6, OsConstants.SEEK_SET));
+            assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5));
+            assertTrue(Arrays.equals("world".getBytes(), readBytes));
+
+            assertEquals(0, Os.lseek(fd.getFileDescriptor(), 0, OsConstants.SEEK_SET));
+            assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5));
+            assertTrue(Arrays.equals("Hello".getBytes(), readBytes));
+        }
+    }
+
+    public void testOpenDocument_shortBytes() throws Exception {
+        mMtpManager = new TestMtpManager(getContext()) {
+            @Override
+            MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
+                if (objectHandle == 1) {
+                    return new MtpObjectInfo.Builder(super.getObjectInfo(deviceId, objectHandle))
+                            .setObjectHandle(1).setCompressedSize(1024 * 1024).build();
+                }
+
+                return super.getObjectInfo(deviceId, objectHandle);
+            }
+        };
+        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+        setupRoots(0, new MtpRoot[] {
+                new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
+        });
+        final byte[] bytes = "Hello world".getBytes();
+        setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
+                new MtpObjectInfo.Builder()
+                        .setName("test.txt")
+                        .setObjectHandle(1)
+                        .setCompressedSize(bytes.length)
+                        .setParent(-1)
+                        .build()
+        });
+        mMtpManager.setImportFileBytes(0, 1, bytes);
+        try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) {
+            final byte[] readBytes = new byte[1024 * 1024];
+            assertEquals(11, Os.read(fd.getFileDescriptor(), readBytes, 0, readBytes.length));
+        }
+    }
+
     private void setupProvider(int flag) {
         mDatabase = new MtpDatabase(getContext(), flag);
         mProvider = new MtpDocumentsProvider();
-        mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase);
+        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+        assertTrue(mProvider.onCreateForTesting(
+                mResources, mMtpManager, mResolver, mDatabase, storageManager));
     }
 
     private String[] getStrings(Cursor cursor) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index a1732dc..1aaeb60 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -208,4 +208,16 @@
     byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException {
         return mImportFileBytes.get(pack(deviceId, objectHandle));
     }
+
+    @Override
+    long getPartialObject(int deviceId, int objectHandle, long offset, long size, byte[] buffer)
+            throws IOException {
+        final byte[] bytes = mImportFileBytes.get(pack(deviceId, objectHandle));
+        int i = 0;
+        while (i < size && i + offset < bytes.length) {
+            buffer[i] = bytes[(int) (i + offset)];
+            i++;
+        }
+        return i;
+    }
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index cd30e26..46a2098 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.printspooler.ui;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Loader;
@@ -452,7 +453,7 @@
         return mPersistenceManager.mReadHistoryCompleted;
     }
 
-    public void setTrackedPrinter(PrinterId printerId) {
+    public void setTrackedPrinter(@Nullable PrinterId printerId) {
         if (isStarted() && mDiscoverySession != null
                 && mDiscoverySession.isPrinterDiscoveryStarted()) {
             if (mTrackedPrinter != null) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 811adda..743df99 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -1003,7 +1003,9 @@
         if (currMediaSize == null) {
             attributes.setMediaSize(defaults.getMediaSize());
         } else {
-            boolean foundCurrentMediaSize = false;
+            MediaSize newMediaSize = null;
+            boolean isPortrait = currMediaSize.isPortrait();
+
             // Try to find the current media size in the capabilities as
             // it may be in a different orientation.
             MediaSize currMediaSizePortrait = currMediaSize.asPortrait();
@@ -1011,14 +1013,21 @@
             for (int i = 0; i < mediaSizeCount; i++) {
                 MediaSize mediaSize = sortedMediaSizes.get(i);
                 if (currMediaSizePortrait.equals(mediaSize.asPortrait())) {
-                    attributes.setMediaSize(currMediaSize);
-                    foundCurrentMediaSize = true;
+                    newMediaSize = mediaSize;
                     break;
                 }
             }
             // If we did not find the current media size fall back to default.
-            if (!foundCurrentMediaSize) {
-                attributes.setMediaSize(defaults.getMediaSize());
+            if (newMediaSize == null) {
+                newMediaSize = defaults.getMediaSize();
+            }
+
+            if (newMediaSize != null) {
+                if (isPortrait) {
+                    attributes.setMediaSize(newMediaSize.asPortrait());
+                } else {
+                    attributes.setMediaSize(newMediaSize.asLandscape());
+                }
             }
         }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
index cbc568a..6d60bb8 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
@@ -16,6 +16,7 @@
 
 package com.android.printspooler.ui;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.Loader;
@@ -89,7 +90,7 @@
         return false;
     }
 
-    public void setTrackedPrinter(PrinterId printerId) {
+    public void setTrackedPrinter(@Nullable PrinterId printerId) {
         FusedPrintersProvider provider = getPrinterProvider();
         if (provider != null) {
             provider.setTrackedPrinter(printerId);
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
index 46267a2..3e1fc4a 100644
--- a/packages/SettingsLib/res/values/attrs.xml
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -16,6 +16,12 @@
 
 <resources>
     <declare-styleable name="RestrictedPreference">
-        <attr name="userRestriction" format="string"/>
+        <attr name="userRestriction" format="string" />
+        <attr name="useAdminDisabledSummary" format="boolean" />
     </declare-styleable>
-</resources>
\ No newline at end of file
+    <declare-styleable name="WifiEncryptionState">
+        <attr name="state_encrypted" format="boolean" />
+    </declare-styleable>
+    <attr name="wifi_signal" format="reference" />
+
+</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 9a1d6a4..811751c 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -35,4 +35,7 @@
     <!-- Lock icon for preferences locked by admin -->
     <dimen name="restricted_lock_icon_size">16dp</dimen>
     <dimen name="restricted_lock_icon_padding">4dp</dimen>
+
+    <dimen name="wifi_preference_badge_padding">8dip</dimen>
+
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6dfa9ad..d3c8416 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -765,4 +765,7 @@
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_status_full">Full</string>
 
+    <!-- Summary for settings preference disabled by administrator [CHAR LIMIT=50] -->
+    <string name="disabled_by_admin_summary_text">Disabled by administrator</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index d69250b..d3c1364 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
+import android.content.pm.UserInfo;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -36,6 +37,8 @@
 import android.view.MenuItem;
 import android.widget.TextView;
 
+import com.android.internal.widget.LockPatternUtils;
+
 import java.util.List;
 
 /**
@@ -60,15 +63,18 @@
      *
      * @param userRestriction Restriction to check
      * @param userId User which we need to check if restriction is enforced on.
-     * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
-     * {@code null} If the restriction is not set. If the restriction is set by both device owner
-     * and profile owner, then the admin will be set to {@code null} and userId to
+     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+     * or {@code null} If the restriction is not set. If the restriction is set by both device owner
+     * and profile owner, then the admin component will be set to {@code null} and userId to
      * {@link UserHandle#USER_NULL}.
      */
     public static EnforcedAdmin checkIfRestrictionEnforced(Context context,
             String userRestriction, int userId) {
         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
+        if (dpm == null) {
+            return null;
+        }
         ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
         int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
         boolean enforcedByDeviceOwner = false;
@@ -109,45 +115,80 @@
     }
 
     /**
-     * Checks if lock screen notification features are disabled by policy. This should be
-     * only used for keyguard notification features but not the keyguard features
-     * (e.g. KEYGUARD_DISABLE_FINGERPRINT) where a profile owner can set them on the parent user
-     * as it won't work for that case.
+     * Checks if keyguard features are disabled by policy.
      *
-     * @param keyguardNotificationFeatures Could be any of notification features that can be
+     * @param keyguardFeatures Could be any of keyguard features that can be
      * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
-     * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
-     * {@code null} If the notification features are not disabled. If the restriction is set by
-     * multiple admins, then the admin will be set to {@code null} and userId to
+     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+     * or {@code null} If the notification features are not disabled. If the restriction is set by
+     * multiple admins, then the admin component will be set to {@code null} and userId to
      * {@link UserHandle#USER_NULL}.
      */
-    public static EnforcedAdmin checkIfKeyguardNotificationFeaturesDisabled(Context context,
-            int keyguardNotificationFeatures) {
+    public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context,
+            int keyguardFeatures) {
         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
-        boolean isDisabledByMultipleAdmins = false;
-        ComponentName adminComponent = null;
-        List<ComponentName> admins = dpm.getActiveAdmins();
-        if (admins != null) {
-            int disabledKeyguardFeatures;
+        if (dpm == null) {
+            return null;
+        }
+        final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
+        EnforcedAdmin enforcedAdmin = null;
+        final int userId = UserHandle.myUserId();
+        if (um.getUserInfo(userId).isManagedProfile()) {
+            final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
+            if (admins == null) {
+                return null;
+            }
             for (ComponentName admin : admins) {
-                disabledKeyguardFeatures = dpm.getKeyguardDisabledFeatures(admin);
-                if ((disabledKeyguardFeatures & keyguardNotificationFeatures) != 0) {
-                    if (adminComponent == null) {
-                        adminComponent = admin;
+                if ((dpm.getKeyguardDisabledFeatures(admin, userId) & keyguardFeatures) != 0) {
+                    if (enforcedAdmin == null) {
+                        enforcedAdmin = new EnforcedAdmin(admin, userId);
                     } else {
-                        isDisabledByMultipleAdmins = true;
-                        break;
+                        return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
                     }
                 }
             }
-        }
-        EnforcedAdmin enforcedAdmin = null;
-        if (adminComponent != null) {
-            if (!isDisabledByMultipleAdmins) {
-                enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId());
-            } else {
-                enforcedAdmin = new EnforcedAdmin();
+        } else {
+            // Consider all admins for this user and the profiles that are visible from this
+            // user that do not use a separate work challenge.
+            for (UserInfo userInfo : um.getProfiles(userId)) {
+                final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
+                if (admins == null) {
+                    return null;
+                }
+                final boolean isSeparateProfileChallengeEnabled =
+                        lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
+                for (ComponentName admin : admins) {
+                    if (!isSeparateProfileChallengeEnabled) {
+                        if ((dpm.getKeyguardDisabledFeatures(admin, userInfo.id)
+                                    & keyguardFeatures) != 0) {
+                            if (enforcedAdmin == null) {
+                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+                            } else {
+                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+                            }
+                            // This same admins could have set policies both on the managed profile
+                            // and on the parent. So, if the admin has set the policy on the
+                            // managed profile here, we don't need to further check if that admin
+                            // has set policy on the parent admin.
+                            continue;
+                        }
+                    }
+                    if (userInfo.isManagedProfile()) {
+                        // If userInfo.id is a managed profile, we also need to look at
+                        // the policies set on the parent.
+                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin);
+                        if ((parentDpm.getKeyguardDisabledFeatures(admin, userInfo.id)
+                                & keyguardFeatures) != 0) {
+                            if (enforcedAdmin == null) {
+                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+                            } else {
+                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+                            }
+                        }
+                    }
+                }
             }
         }
         return enforcedAdmin;
@@ -170,6 +211,9 @@
             if (ipm.getBlockUninstallForUser(packageName, userId)) {
                 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                         Context.DEVICE_POLICY_SERVICE);
+                if (dpm == null) {
+                    return null;
+                }
                 ComponentName admin = dpm.getProfileOwner();
                 if (admin == null) {
                     admin = dpm.getDeviceOwnerComponentOnCallingUser();
@@ -197,6 +241,9 @@
         }
         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
+        if (dpm == null) {
+            return null;
+        }
         boolean isAccountTypeDisabled = false;
         String[] disabledTypes = dpm.getAccountTypesWithManagementDisabled();
         for (String type : disabledTypes) {
@@ -221,7 +268,7 @@
     public static EnforcedAdmin checkIfAutoTimeRequired(Context context) {
         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
-        if (dpm.getAutoTimeRequired()) {
+        if (dpm == null || !dpm.getAutoTimeRequired()) {
             return null;
         }
         ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
@@ -240,6 +287,9 @@
     public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context) {
         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
+        if (dpm == null) {
+            return null;
+        }
         boolean isDisabledByMultipleAdmins = false;
         ComponentName adminComponent = null;
         List<ComponentName> admins = dpm.getActiveAdmins();
@@ -268,9 +318,84 @@
         return enforcedAdmin;
     }
 
+    /**
+     * Checks if any admin has set maximum time to lock.
+     *
+     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+     * or {@code null} if no admin has set this restriction. If multiple admins has set this, then
+     * the admin component will be set to {@code null} and userId to {@link UserHandle#USER_NULL}
+     */
+    public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) {
+        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
+        EnforcedAdmin enforcedAdmin = null;
+        final int userId = UserHandle.myUserId();
+        if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+            // If the user has a separate challenge, only consider the admins in that user.
+            final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
+            if (admins == null) {
+                return null;
+            }
+            for (ComponentName admin : admins) {
+                if (dpm.getMaximumTimeToLock(admin, userId) > 0) {
+                    if (enforcedAdmin == null) {
+                        enforcedAdmin = new EnforcedAdmin(admin, userId);
+                    } else {
+                        return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+                    }
+                }
+            }
+        } else {
+            // Return all admins for this user and the profiles that are visible from this
+            // user that do not use a separate work challenge.
+            final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+            for (UserInfo userInfo : um.getProfiles(userId)) {
+                final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
+                if (admins == null) {
+                    return null;
+                }
+                final boolean isSeparateProfileChallengeEnabled =
+                        lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
+                for (ComponentName admin : admins) {
+                    if (!isSeparateProfileChallengeEnabled) {
+                        if (dpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
+                            if (enforcedAdmin == null) {
+                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+                            } else {
+                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+                            }
+                            // This same admins could have set policies both on the managed profile
+                            // and on the parent. So, if the admin has set the policy on the
+                            // managed profile here, we don't need to further check if that admin
+                            // has set policy on the parent admin.
+                            continue;
+                        }
+                    }
+                    if (userInfo.isManagedProfile()) {
+                        // If userInfo.id is a managed profile, we also need to look at
+                        // the policies set on the parent.
+                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin);
+                        if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
+                            if (enforcedAdmin == null) {
+                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+                            } else {
+                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return enforcedAdmin;
+    }
+
     public static EnforcedAdmin getProfileOrDeviceOwnerOnCallingUser(Context context) {
         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
+        if (dpm == null) {
+            return null;
+        }
         ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
         if (adminComponent != null) {
             return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
@@ -391,6 +516,9 @@
         public ComponentName component = null;
         public int userId = UserHandle.USER_NULL;
 
+        // We use this to represent the case where a policy is enforced by multiple admins.
+        public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin();
+
         public EnforcedAdmin(ComponentName component, int userId) {
             this.component = component;
             this.userId = userId;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 13a46d0..810f6eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -65,6 +65,10 @@
         }
     }
 
+    public void useAdminDisabledSummary(boolean useSummary) {
+        mHelper.useAdminDisabledSummary(useSummary);
+    }
+
     @Override
     protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
         mHelper.onAttachedToHierarchy();
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 06aba96..9bd4eb1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -27,6 +27,7 @@
 import android.text.style.ImageSpan;
 import android.util.AttributeSet;
 import android.util.TypedValue;
+import android.view.View;
 import android.widget.TextView;
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -44,6 +45,7 @@
     private boolean mDisabledByAdmin;
     private EnforcedAdmin mEnforcedAdmin;
     private String mAttrUserRestriction = null;
+    private boolean mUseAdminDisabledSummary = false;
 
     public RestrictedPreferenceHelper(Context context, Preference preference,
             AttributeSet attrs) {
@@ -68,6 +70,14 @@
                 }
             }
             mAttrUserRestriction = data == null ? null : data.toString();
+
+            final TypedValue useAdminDisabledSummary =
+                    attributes.peekValue(R.styleable.RestrictedPreference_useAdminDisabledSummary);
+            if (useAdminDisabledSummary != null) {
+                mUseAdminDisabledSummary =
+                        (useAdminDisabledSummary.type == TypedValue.TYPE_INT_BOOLEAN
+                                && useAdminDisabledSummary.data != 0);
+            }
         }
     }
 
@@ -82,6 +92,21 @@
                 holder.itemView.setEnabled(true);
             }
         }
+        if (mUseAdminDisabledSummary) {
+            final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
+            if (summaryView != null) {
+                if (mDisabledByAdmin) {
+                    summaryView.setText(R.string.disabled_by_admin_summary_text);
+                    summaryView.setVisibility(View.VISIBLE);
+                } else {
+                    summaryView.setVisibility(View.GONE);
+                }
+            }
+        }
+    }
+
+    public void useAdminDisabledSummary(boolean useSummary) {
+        mUseAdminDisabledSummary = useSummary;
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 84e2bff..6cae8aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -65,6 +65,10 @@
         }
     }
 
+    public void useAdminDisabledSummary(boolean useSummary) {
+        mHelper.useAdminDisabledSummary(useSummary);
+    }
+
     @Override
     protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
         mHelper.onAttachedToHierarchy();
diff --git a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
index 58a477e..1859207 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -38,14 +39,39 @@
 
     private static final String TAG = "SuggestionParser";
 
+    // If defined, only returns this suggestion if the feature is supported.
+    public static final String META_DATA_REQUIRE_FEATURE = "com.android.settings.require_feature";
+
+    /**
+     * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed.
+     * For instance:
+     * 0,10
+     * Will appear immediately, but if the user removes it, it will come back after 10 days.
+     *
+     * Another example:
+     * 10,30
+     * Will only show up after 10 days, and then again after 30.
+     */
+    public static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss";
+
+    // Shared prefs keys for storing dismissed state.
+    // Index into current dismissed state.
+    private static final String DISMISS_INDEX = "_dismiss_index";
+    private static final String SETUP_TIME = "_setup_time";
+    private static final String IS_DISMISSED = "_is_dismissed";
+
+    private static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
+
     private final Context mContext;
     private final List<SuggestionCategory> mSuggestionList;
     private final ArrayMap<Pair<String, String>, Tile> addCache = new ArrayMap<>();
+    private final SharedPreferences mSharedPrefs;
 
-    public SuggestionParser(Context context, int orderXml) {
+    public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
         mContext = context;
         mSuggestionList = (List<SuggestionCategory>) new SuggestionOrderInflater(mContext)
                 .parse(orderXml);
+        mSharedPrefs = sharedPrefs;
     }
 
     public List<Tile> getSuggestions() {
@@ -57,6 +83,23 @@
         return suggestions;
     }
 
+    /**
+     * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
+     * be disabled.
+     */
+    public boolean dismissSuggestion(Tile suggestion) {
+        String keyBase = suggestion.intent.getComponent().flattenToShortString();
+        int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
+        String dismissControl = suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
+        if (dismissControl == null || parseDismissString(dismissControl).length == index) {
+            return true;
+        }
+        mSharedPrefs.edit()
+                .putBoolean(keyBase + IS_DISMISSED, true)
+                .commit();
+        return false;
+    }
+
     private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) {
         int countBefore = suggestions.size();
         Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -66,6 +109,11 @@
         }
         TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
                 addCache, null, suggestions, true, false);
+        for (int i = countBefore; i < suggestions.size(); i++) {
+            if (!isAvailable(suggestions.get(i)) || isDismissed(suggestions.get(i))) {
+                suggestions.remove(i--);
+            }
+        }
         if (!category.multiple && suggestions.size() > (countBefore + 1)) {
             // If there are too many, remove them all and only re-add the one with the highest
             // priority.
@@ -80,6 +128,59 @@
         }
     }
 
+    private boolean isAvailable(Tile suggestion) {
+        String featureRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE);
+        if (featureRequired != null) {
+            return mContext.getPackageManager().hasSystemFeature(featureRequired);
+        }
+        return true;
+    }
+
+    private boolean isDismissed(Tile suggestion) {
+        Object dismissObj = suggestion.metaData.get(META_DATA_DISMISS_CONTROL);
+        if (dismissObj == null) {
+            return false;
+        }
+        String dismissControl = String.valueOf(dismissObj);
+        String keyBase = suggestion.intent.getComponent().flattenToShortString();
+        if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
+            mSharedPrefs.edit()
+                    .putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
+                    .commit();
+        }
+        // Default to dismissed, so that we can have suggestions that only first appear after
+        // some number of days.
+        if (!mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, true)) {
+            return false;
+        }
+        int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
+        int currentDismiss = parseDismissString(dismissControl)[index];
+        long time = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0), currentDismiss);
+        if (System.currentTimeMillis() >= time) {
+            // Dismiss timeout has passed, undismiss it.
+            mSharedPrefs.edit()
+                    .putBoolean(keyBase + IS_DISMISSED, false)
+                    .putInt(keyBase + DISMISS_INDEX, index + 1)
+                    .commit();
+            return false;
+        }
+        return true;
+    }
+
+    private long getEndTime(long startTime, int daysDelay) {
+        long days = daysDelay * MILLIS_IN_DAY;
+        return startTime + days;
+    }
+
+    private int[] parseDismissString(String dismissControl) {
+        String[] dismissStrs = dismissControl.split(",");
+        int[] dismisses = new int[dismissStrs.length];
+        for (int i = 0; i < dismissStrs.length; i++) {
+            dismisses[i] = Integer.parseInt(dismissStrs[i]);
+        }
+        return dismisses;
+    }
+
     private static class SuggestionCategory {
         public String category;
         public String pkg;
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index 66233b8..f5a2aae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -29,12 +29,6 @@
 
 public class TetherUtil {
 
-    // Types of tethering.
-    public static final int TETHERING_INVALID   = -1;
-    public static final int TETHERING_WIFI      = 0;
-    public static final int TETHERING_USB       = 1;
-    public static final int TETHERING_BLUETOOTH = 2;
-
     // Extras used for communicating with the TetherService.
     public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
     public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
@@ -43,14 +37,6 @@
      * Tells the service to run a provision check now.
      */
     public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
-    /**
-     * Enables wifi tethering if the provision check is successful. Used by
-     * QS to enable tethering.
-     */
-    public static final String EXTRA_ENABLE_WIFI_TETHER = "extraEnableWifiTether";
-
-    public static ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
-            .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
 
     public static boolean setWifiTethering(boolean enable, Context context) {
         final WifiManager wifiManager =
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
index 2fd043f..dc6002d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
@@ -61,6 +61,8 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         UserHandle user = mSelectedTile.userHandle.get(which);
+        // Show menu on top level items.
+        mSelectedTile.intent.putExtra(SettingsDrawerActivity.EXTRA_SHOW_MENU, true);
         getActivity().startActivityAsUser(mSelectedTile.intent, user);
         ((SettingsDrawerActivity) getActivity()).onProfileTileOpen();
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 3fc0c22..56c4edb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -48,6 +48,8 @@
     protected static final boolean DEBUG_TIMING = false;
     private static final String TAG = "SettingsDrawerActivity";
 
+    static final String EXTRA_SHOW_MENU = "show_drawer_menu";
+
     private static List<DashboardCategory> sDashboardCategories;
     private static HashMap<Pair<String, String>, Tile> sTileCache;
 
@@ -56,6 +58,7 @@
 
     private SettingsDrawerAdapter mDrawerAdapter;
     private DrawerLayout mDrawerLayout;
+    private boolean mShowingMenu;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -94,7 +97,7 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        if (mDrawerLayout != null && item.getItemId() == android.R.id.home
+        if (mShowingMenu && mDrawerLayout != null && item.getItemId() == android.R.id.home
                 && mDrawerAdapter.getCount() != 0) {
             openDrawer();
             return true;
@@ -116,6 +119,9 @@
 
             new CategoriesUpdater().execute();
         }
+        if (getIntent() != null && getIntent().getBooleanExtra(EXTRA_SHOW_MENU, false)) {
+            showMenuIcon();
+        }
     }
 
     @Override
@@ -171,13 +177,17 @@
         mDrawerAdapter.updateCategories();
         if (mDrawerAdapter.getCount() != 0) {
             mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
-            getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu);
-            getActionBar().setDisplayHomeAsUpEnabled(true);
         } else {
             mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
         }
     }
 
+    public void showMenuIcon() {
+        mShowingMenu = true;
+        getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu);
+        getActionBar().setDisplayHomeAsUpEnabled(true);
+    }
+
     public List<DashboardCategory> getDashboardCategories() {
         if (sDashboardCategories == null) {
             sTileCache = new HashMap<>();
@@ -204,8 +214,12 @@
             ProfileSelectDialog.show(getFragmentManager(), tile);
             return false;
         } else if (numUserHandles == 1) {
+            // Show menu on top level items.
+            tile.intent.putExtra(EXTRA_SHOW_MENU, true);
             startActivityAsUser(tile.intent, tile.userHandle.get(0));
         } else {
+            // Show menu on top level items.
+            tile.intent.putExtra(EXTRA_SHOW_MENU, true);
             startActivity(tile.intent);
         }
         return true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 5d8668b..e53dd2f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -228,7 +228,7 @@
     private static String getActiveSubscriberId(Context context) {
         final TelephonyManager tele = TelephonyManager.from(context);
         final String actualSubscriberId = tele.getSubscriberId(
-                SubscriptionManager.getDefaultDataSubId());
+                SubscriptionManager.getDefaultDataSubscriptionId());
         return actualSubscriberId;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
new file mode 100644
index 0000000..284827b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.wifi;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
+import android.net.wifi.WifiConfiguration;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.widget.TextView;
+
+import com.android.settingslib.R;
+
+public class AccessPointPreference extends Preference {
+
+    private static final int[] STATE_SECURED = {
+            R.attr.state_encrypted
+    };
+    private static final int[] STATE_NONE = {};
+
+    private static int[] wifi_signal_attributes = { R.attr.wifi_signal };
+
+    private final StateListDrawable mWifiSld;
+    private final int mBadgePadding;
+    private final UserBadgeCache mBadgeCache;
+
+    private TextView mTitleView;
+    private boolean mForSavedNetworks = false;
+    private AccessPoint mAccessPoint;
+    private Drawable mBadge;
+    private int mLevel;
+    private CharSequence mContentDescription;
+
+    static final int[] WIFI_CONNECTION_STRENGTH = {
+            R.string.accessibility_wifi_one_bar,
+            R.string.accessibility_wifi_two_bars,
+            R.string.accessibility_wifi_three_bars,
+            R.string.accessibility_wifi_signal_full
+    };
+
+    // Used for dummy pref.
+    public AccessPointPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mWifiSld = null;
+        mBadgePadding = 0;
+        mBadgeCache = null;
+    }
+
+    public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
+            boolean forSavedNetworks) {
+        super(context);
+        mBadgeCache = cache;
+        mAccessPoint = accessPoint;
+        mForSavedNetworks = forSavedNetworks;
+        mAccessPoint.setTag(this);
+        mLevel = -1;
+
+        mWifiSld = (StateListDrawable) context.getTheme()
+                .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
+
+        // Distance from the end of the title at which this AP's user badge should sit.
+        mBadgePadding = context.getResources()
+                .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
+        refresh();
+    }
+
+    public AccessPoint getAccessPoint() {
+        return mAccessPoint;
+    }
+
+    @Override
+    public void onBindViewHolder(final PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+        if (mAccessPoint == null) {
+            // Used for dummy pref.
+            return;
+        }
+        Drawable drawable = getIcon();
+        if (drawable != null) {
+            drawable.setLevel(mLevel);
+        }
+
+        mTitleView = (TextView) view.findViewById(com.android.internal.R.id.title);
+        if (mTitleView != null) {
+            // Attach to the end of the title view
+            mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null);
+            mTitleView.setCompoundDrawablePadding(mBadgePadding);
+        }
+        view.itemView.setContentDescription(mContentDescription);
+    }
+
+    protected void updateIcon(int level, Context context) {
+        if (level == -1) {
+            setIcon(null);
+        } else {
+            if (getIcon() == null) {
+                // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
+                // set the icon (drawable) to that state's drawable.
+                // If sld is null then we are indexing and therefore do not have access to
+                // (nor need to display) the drawable.
+                if (mWifiSld != null) {
+                    mWifiSld.setState((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
+                            ? STATE_SECURED
+                            : STATE_NONE);
+                    Drawable drawable = mWifiSld.getCurrent();
+                    if (!mForSavedNetworks) {
+                        setIcon(drawable);
+                    } else {
+                        setIcon(null);
+                    }
+                }
+            }
+        }
+    }
+
+    protected void updateBadge(Context context) {
+        WifiConfiguration config = mAccessPoint.getConfig();
+        if (config != null) {
+            // Fetch badge (may be null)
+            // Get the badge using a cache since the PM will ask the UserManager for the list
+            // of profiles every time otherwise.
+            mBadge = mBadgeCache.getUserBadge(config.creatorUid);
+        }
+    }
+
+    /**
+     * Updates the title and summary; may indirectly call notifyChanged().
+     */
+    public void refresh() {
+        if (mForSavedNetworks) {
+            setTitle(mAccessPoint.getConfigName());
+        } else {
+            setTitle(mAccessPoint.getSsid());
+        }
+
+        final Context context = getContext();
+        int level = mAccessPoint.getLevel();
+        if (level != mLevel) {
+            mLevel = level;
+            updateIcon(mLevel, context);
+            notifyChanged();
+        }
+        updateBadge(context);
+
+        setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
+                : mAccessPoint.getSettingsSummary());
+
+        mContentDescription = getTitle();
+        if (getSummary() != null) {
+            mContentDescription = TextUtils.concat(mContentDescription, ",", getSummary());
+        }
+        if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
+            mContentDescription = TextUtils.concat(mContentDescription, ",",
+                    getContext().getString(WIFI_CONNECTION_STRENGTH[level]));
+        }
+    }
+
+    @Override
+    protected void notifyChanged() {
+        if (Looper.getMainLooper() != Looper.myLooper()) {
+            // Let our BG thread callbacks call setTitle/setSummary.
+            postNotifyChanged();
+        } else {
+            super.notifyChanged();
+        }
+    }
+
+    public void onLevelChanged() {
+        postNotifyChanged();
+    }
+
+    private void postNotifyChanged() {
+        if (mTitleView != null) {
+            mTitleView.post(mNotifyChanged);
+        } // Otherwise we haven't been bound yet, and don't need to update.
+    }
+
+    private final Runnable mNotifyChanged = new Runnable() {
+        @Override
+        public void run() {
+            notifyChanged();
+        }
+    };
+
+    public static class UserBadgeCache {
+        private final SparseArray<Drawable> mBadges = new SparseArray<>();
+        private final PackageManager mPm;
+
+        public UserBadgeCache(PackageManager pm) {
+            mPm = pm;
+        }
+
+        private Drawable getUserBadge(int userId) {
+            int index = mBadges.indexOfKey(userId);
+            if (index < 0) {
+                Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
+                mBadges.put(userId, badge);
+                return badge;
+            }
+            return mBadges.valueAt(index);
+        }
+    }
+}
diff --git a/packages/SystemUI/res/drawable/brightness_mirror_background.xml b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
index fadfe63..e901e40 100644
--- a/packages/SystemUI/res/drawable/brightness_mirror_background.xml
+++ b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
@@ -16,9 +16,4 @@
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="@color/system_primary_color" />
-    <corners
-        android:topLeftRadius="@dimen/notification_material_rounded_rect_radius"
-        android:topRightRadius="@dimen/notification_material_rounded_rect_radius"
-        android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius"
-        android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/>
 </shape>
diff --git a/packages/SystemUI/res/drawable/ic_data_saver.xml b/packages/SystemUI/res/drawable/ic_data_saver.xml
new file mode 100644
index 0000000..426238c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_data_saver.xml
@@ -0,0 +1,28 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="
+        M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z
+        m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z
+        m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z
+        m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_data_saver_off.xml b/packages/SystemUI/res/drawable/ic_data_saver_off.xml
new file mode 100644
index 0000000..0713548
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_data_saver_off.xml
@@ -0,0 +1,28 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#4DFFFFFF"
+        android:pathData="
+        M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z
+        m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z
+        m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z
+        m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 6a0277f..ae45663 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -20,7 +20,6 @@
     <item>
         <shape>
             <solid android:color="@color/notification_material_background_color" />
-            <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
         </shape>
     </item>
 </ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
index 6581942..b6a8b70 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
@@ -18,7 +18,6 @@
     <item>
         <shape>
             <solid android:color="@color/notification_material_background_dimmed_color" />
-            <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
         </shape>
     </item>
 </ripple>
diff --git a/packages/SystemUI/res/drawable/qs_background_primary.xml b/packages/SystemUI/res/drawable/qs_background_primary.xml
index 686df2c..1bf7d4c 100644
--- a/packages/SystemUI/res/drawable/qs_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_background_primary.xml
@@ -13,10 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetTop="@dimen/notification_material_rounded_rect_radius_negative">
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
     <shape>
         <solid android:color="@color/system_primary_color"/>
-        <corners android:radius="@dimen/notification_material_rounded_rect_radius"/>
     </shape>
 </inset>
diff --git a/packages/SystemUI/res/drawable/qs_background_secondary.xml b/packages/SystemUI/res/drawable/qs_background_secondary.xml
index 3662e5a..31c0162 100644
--- a/packages/SystemUI/res/drawable/qs_background_secondary.xml
+++ b/packages/SystemUI/res/drawable/qs_background_secondary.xml
@@ -17,7 +17,5 @@
     <solid android:color="@color/system_secondary_color" />
     <corners
         android:topLeftRadius="0dp"
-        android:topRightRadius="0dp"
-        android:bottomLeftRadius="@dimen/notification_material_rounded_rect_radius"
-        android:bottomRightRadius="@dimen/notification_material_rounded_rect_radius"/>
+        android:topRightRadius="0dp" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
index f09c01b..e98bfb8 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -14,9 +14,5 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android" >
-
     <solid android:color="@color/system_primary_color" />
-
-    <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
-
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/apps_bar.xml b/packages/SystemUI/res/layout/apps_bar.xml
deleted file mode 100644
index e226805..0000000
--- a/packages/SystemUI/res/layout/apps_bar.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
--->
-
-<!-- Container for the app shelf. -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/app_shelf"
-    android:orientation="horizontal"
-    android:layout_height="match_parent"
-    android:layout_width="0dp"
-    android:layout_weight="1">
-    <com.android.systemui.statusbar.phone.NavigationBarApps
-        android:id="@+id/navigation_bar_apps"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
index 9ef743d..6e4b213 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -33,7 +33,8 @@
             android:paddingTop="@dimen/car_lockscreen_disclaimer_title_padding_top" />
         <TextView
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="1"
             android:text="@string/car_lockscreen_disclaimer_text"
             android:textSize="@dimen/car_lockscreen_disclaimer_text_size"
             android:paddingStart="@dimen/car_lockscreen_disclaimer_text_padding_start"
@@ -44,7 +45,6 @@
             android:layout_gravity="center"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:paddingTop="@dimen/car_lockscreen_user_grid_view_padding_top"
             android:stretchMode="columnWidth">
         </com.android.systemui.statusbar.car.UserGridView>
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml
index 479f18d..7677646 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/SystemUI/res/layout/car_navigation_button.xml
@@ -26,9 +26,10 @@
     <com.android.keyguard.AlphaOptimizedImageButton
             android:id="@+id/car_nav_button_icon"
             android:layout_height="match_parent"
-            android:layout_width="wrap_content"
+            android:layout_width="@dimen/car_navigation_button_width"
             android:layout_centerInParent="true"
-            android:animateLayoutChanges="true">
+            android:animateLayoutChanges="true"
+            android:scaleType="fitCenter">
     </com.android.keyguard.AlphaOptimizedImageButton>
 
     <com.android.keyguard.AlphaOptimizedImageButton
@@ -37,6 +38,7 @@
             android:layout_width="wrap_content"
             android:layout_centerVertical="true"
             android:layout_toRightOf="@+id/car_nav_button_icon"
-            android:animateLayoutChanges="true">
+            android:animateLayoutChanges="true"
+            android:scaleType="fitCenter">
     </com.android.keyguard.AlphaOptimizedImageButton>
 </com.android.systemui.statusbar.car.CarNavigationButton>
diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml
index 53273cf..dad7cea 100644
--- a/packages/SystemUI/res/layout/notification_children_divider.xml
+++ b/packages/SystemUI/res/layout/notification_children_divider.xml
@@ -19,5 +19,5 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/notification_more_divider"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/notification_children_divider_height"
+    android:layout_height="@dimen/notification_divider_height"
     android:background="#61000000" />
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index f430fa5..e56431b 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -87,9 +87,7 @@
             android:id="@+id/quick_settings_panel"
             android:background="#0000"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="@dimen/notification_side_padding"
-            android:layout_marginRight="@dimen/notification_side_padding" />
+            android:layout_height="wrap_content" />
 
     </com.android.systemui.tuner.AutoScrollView>
 
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 2377684..6784695 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -23,8 +23,6 @@
     android:layout_width="@dimen/notification_panel_width"
     android:layout_height="@dimen/status_bar_header_height"
     android:layout_gravity="@integer/notification_panel_layout_gravity"
-    android:paddingStart="@dimen/notification_side_padding"
-    android:paddingEnd="@dimen/notification_side_padding"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:baselineAligned="false"
@@ -70,6 +68,18 @@
 
         </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
 
+        <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_alignParentEnd="true"
+            android:background="@drawable/ripple_drawable" >
+            <ImageView android:id="@+id/multi_user_avatar"
+                android:layout_width="@dimen/multi_user_avatar_expanded_size"
+                android:layout_height="@dimen/multi_user_avatar_expanded_size"
+                android:layout_gravity="center"
+                android:scaleType="centerInside"/>
+        </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
         <ImageView
             android:layout_width="48dp"
             android:layout_height="48dp"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 12cf137..89abe2d 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,20 +55,13 @@
                     layout="@layout/qs_panel"
                     android:layout_marginTop="@dimen/status_bar_header_height_expanded"
                     android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_marginLeft="@dimen/notification_side_padding"
-                    android:layout_marginRight="@dimen/notification_side_padding"/>
+                    android:layout_height="wrap_content" />
 
                 <!-- A view to reserve space for the collapsed stack -->
                 <!-- Layout height: notification_min_height + bottom_stack_peek_amount -->
                 <View
                     android:id="@+id/reserve_notification_space"
                     android:layout_height="@dimen/min_stack_height"
-                    android:layout_width="match_parent"
-                    android:layout_marginTop="@dimen/notifications_top_padding" />
-
-                <View
-                    android:layout_height="@dimen/notification_side_padding"
                     android:layout_width="match_parent" />
             </LinearLayout>
         </com.android.systemui.statusbar.phone.ObservableScrollView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 5eca471..dd75dbf 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -23,8 +23,6 @@
     android:layout_width="@dimen/notification_panel_width"
     android:layout_height="@dimen/status_bar_header_height"
     android:layout_gravity="@integer/notification_panel_layout_gravity"
-    android:paddingStart="@dimen/notification_side_padding"
-    android:paddingEnd="@dimen/notification_side_padding"
     android:baselineAligned="false"
     android:elevation="4dp"
     android:background="@drawable/notification_header_bg"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml
deleted file mode 100644
index e220a16..0000000
--- a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<!-- Extends FrameLayout -->
-<com.android.systemui.statusbar.SpeedBumpView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/speed_bump_height"
-    android:visibility="gone"
-    >
-    <com.android.systemui.statusbar.AlphaOptimizedView
-        android:id="@+id/speedbump_line"
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:background="#6fdddddd"
-        android:layout_gravity="center_vertical"/>
-</com.android.systemui.statusbar.SpeedBumpView>
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 4c80b48..3dca77d 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -65,8 +65,6 @@
                  android:layout_width="@dimen/notification_panel_width"
                  android:layout_height="wrap_content"
                  android:layout_gravity="@integer/notification_panel_layout_gravity"
-                 android:paddingLeft="@dimen/notification_side_padding"
-                 android:paddingRight="@dimen/notification_side_padding"
                  android:visibility="invisible">
         <FrameLayout
                 android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 7cbc55c..34796cd 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -19,8 +19,6 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginBottom="@dimen/volume_dialog_margin_bottom"
-    android:layout_marginLeft="@dimen/notification_side_padding"
-    android:layout_marginRight="@dimen/notification_side_padding"
     android:background="@drawable/volume_dialog_background"
     android:translationZ="4dp" >
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b2190ec..5e25d2c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -78,14 +78,13 @@
     <color name="notification_material_background_color">#ffffffff</color>
 
     <!-- The color of the material notification background when dimmed -->
-    <color name="notification_material_background_dimmed_color">#f2ffffff</color>
+    <color name="notification_material_background_dimmed_color">#ccffffff</color>
 
     <!-- The color of the material notification background when low priority -->
     <color name="notification_material_background_low_priority_color">#fff5f5f5</color>
 
-    <!-- The color of the material notification background for media notifications when no custom
-         color is specified -->
-    <color name="notification_material_background_media_default_color">#ff424242</color>
+    <!-- The background color of the notification shade -->
+    <color name="notification_shade_background_color">#ffeeeeee</color>
 
     <!-- The color of the ripples on the untinted notifications -->
     <color name="notification_ripple_untinted_color">#28000000</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 46a0f2a..e245c24 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -87,9 +87,6 @@
     <!-- gap on either side of status bar notification icons -->
     <dimen name="status_bar_icon_padding">0dp</dimen>
 
-    <!-- half the distance between notifications in the panel -->
-    <dimen name="notification_divider_height">2dp</dimen>
-
     <!-- The padding on the global screenshot background image -->
     <dimen name="global_screenshot_bg_padding">20dp</dimen>
 
@@ -137,7 +134,7 @@
 
     <!-- Width for the notification panel and related windows -->
     <dimen name="match_parent">-1px</dimen>
-    <dimen name="standard_notification_panel_width">416dp</dimen><!-- includes notification_side_padding on each side -->
+    <dimen name="standard_notification_panel_width">416dp</dimen>
     <dimen name="notification_panel_width">@dimen/match_parent</dimen>
 
     <!-- Gravity for the notification panel -->
@@ -298,17 +295,14 @@
     <!-- The height of the area before the top stack in which the notifications slow down -->
     <dimen name="top_stack_slow_down_length">12dp</dimen>
 
-    <!-- The side padding of the notifications-->
-    <dimen name="notification_side_padding">8dp</dimen>
-
     <!-- Z distance between notifications if they are in the stack -->
-    <dimen name="z_distance_between_notifications">1dp</dimen>
+    <dimen name="z_distance_between_notifications">0.5dp</dimen>
 
-    <!-- The padding between the individual notification cards when dimmed. -->
-    <dimen name="notification_padding_dimmed">0dp</dimen>
+    <!-- The height of the divider between the individual notifications. -->
+    <dimen name="notification_divider_height">0.5dp</dimen>
 
-    <!-- The padding between the individual notification cards. -->
-    <dimen name="notification_padding">2dp</dimen>
+    <!-- The height of the divider between the individual notifications when the notification wants it to be increased. This is currently the case for notification groups -->
+    <dimen name="notification_divider_height_increased">6dp</dimen>
 
     <!-- The minimum amount of top overscroll to go to the quick settings. -->
     <dimen name="min_top_overscroll_to_qs">36dp</dimen>
@@ -328,8 +322,6 @@
     <!-- Falsing threshold used when dismissing notifications from the lockscreen. -->
     <dimen name="swipe_helper_falsing_threshold">70dp</dimen>
 
-    <dimen name="notifications_top_padding">4dp</dimen>
-
     <!-- Minimum distance the user has to drag down to go to the full shade. -->
     <dimen name="keyguard_drag_down_min_distance">100dp</dimen>
 
@@ -374,21 +366,12 @@
          phone hints. -->
     <dimen name="edge_tap_area_width">48dp</dimen>
 
-    <!-- radius of the corners of the material rounded rect background -->
-    <dimen name="notification_material_rounded_rect_radius">2dp</dimen>
-
-    <!-- radius of the corners of the material rounded rect background but negative-->
-    <dimen name="notification_material_rounded_rect_radius_negative">-2dp</dimen>
-
     <!-- The padding between notification children when collapsed -->
     <dimen name="notification_children_padding">4dp</dimen>
 
     <!-- The padding on top of the first notification to the children container -->
     <dimen name="notification_children_container_top_padding">8dp</dimen>
 
-    <!-- The height of the divider between the notfication children -->
-    <dimen name="notification_children_divider_height">1dp</dimen>
-
     <!-- The vertical distance from which the notification appear when children are expanded -->
     <dimen name="notification_appear_distance">140dp</dimen>
 
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml
index ecdccee..04402b7 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/SystemUI/res/values/dimens_car.xml
@@ -18,15 +18,15 @@
 <resources>
     <dimen name="car_lockscreen_disclaimer_title_size">48sp</dimen>
     <dimen name="car_lockscreen_disclaimer_title_padding_start">96dp</dimen>
-    <dimen name="car_lockscreen_disclaimer_title_padding_top">96dp</dimen>
+    <dimen name="car_lockscreen_disclaimer_title_padding_top">82dp</dimen>
     <dimen name="car_lockscreen_disclaimer_text_size">28sp</dimen>
     <dimen name="car_lockscreen_disclaimer_text_padding_start">96dp</dimen>
     <dimen name="car_lockscreen_disclaimer_text_padding_end">96dp</dimen>
-    <dimen name="car_lockscreen_disclaimer_text_padding_top">32dp</dimen>
+    <dimen name="car_lockscreen_disclaimer_text_padding_top">8dp</dimen>
     <dimen name="car_lockscreen_user_grid_view_padding_start">10dp</dimen>
     <dimen name="car_lockscreen_user_grid_view_padding_end">10dp</dimen>
-    <dimen name="car_lockscreen_user_grid_view_padding_top">128dp</dimen>
     <dimen name="car_fullscreen_user_pod_image_avatar_width">128dp</dimen>
     <dimen name="car_fullscreen_user_pod_image_avatar_height">128dp</dimen>
     <dimen name="car_fullscreen_user_pod_text_size">24sp</dimen>
+    <dimen name="car_navigation_button_width">64dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 0bd350b..87aedab 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -18,22 +18,22 @@
 <resources>
     <item type="id" name="translation_y_animator_tag"/>
     <item type="id" name="translation_z_animator_tag"/>
-    <item type="id" name="scale_animator_tag"/>
     <item type="id" name="alpha_animator_tag"/>
     <item type="id" name="top_inset_animator_tag"/>
     <item type="id" name="height_animator_tag"/>
+    <item type="id" name="shadow_alpha_animator_tag"/>
     <item type="id" name="translation_y_animator_end_value_tag"/>
     <item type="id" name="translation_z_animator_end_value_tag"/>
-    <item type="id" name="scale_animator_end_value_tag"/>
     <item type="id" name="alpha_animator_end_value_tag"/>
     <item type="id" name="top_inset_animator_end_value_tag"/>
     <item type="id" name="height_animator_end_value_tag"/>
+    <item type="id" name="shadow_alpha_animator_end_value_tag"/>
     <item type="id" name="translation_y_animator_start_value_tag"/>
     <item type="id" name="translation_z_animator_start_value_tag"/>
-    <item type="id" name="scale_animator_start_value_tag"/>
     <item type="id" name="alpha_animator_start_value_tag"/>
     <item type="id" name="top_inset_animator_start_value_tag"/>
     <item type="id" name="height_animator_start_value_tag"/>
+    <item type="id" name="shadow_alpha_animator_start_value_tag"/>
     <item type="id" name="doze_saved_filter_tag"/>
     <item type="id" name="qs_icon_tag"/>
     <item type="id" name="scrim"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 814e7ea..faf36ec 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1333,4 +1333,13 @@
     <!-- Explanation of the status bar section of the tuner [CHAR LIMIT=NONE] -->
     <string name="tuner_status_bar_explanation">Enable or disable icons from being shown in the status bar.</string>
 
+    <!-- Label for quick settings tile for data saver [CHAR LIMIT=30] -->
+    <string name="data_saver">Data Saver</string>
+
+    <!-- Accessibility description for data saver being on [CHAR LIMIT=NONE] -->
+    <string name="accessibility_data_saver_on">Data Saver is on</string>
+
+    <!-- Accessibility description for data saver being off [CHAR LIMIT=NONE] -->
+    <string name="accessibility_data_saver_off">Data Saver is off</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index fd4161f..5b955a4 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -30,11 +30,9 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
 
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.Interpolators;
 
 public class SwipeHelper implements Gefingerpoken {
     static final String TAG = "com.android.systemui.SwipeHelper";
@@ -48,9 +46,6 @@
     public static final int X = 0;
     public static final int Y = 1;
 
-    private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
-    private final Interpolator mFastOutLinearInInterpolator;
-
     private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
     private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
     private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
@@ -97,8 +92,6 @@
         mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
 
         mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); // extra long-press!
-        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
-                android.R.interpolator.fast_out_linear_in);
         mFalsingThreshold = context.getResources().getDimensionPixelSize(
                 R.dimen.swipe_helper_falsing_threshold);
         mFalsingManager = FalsingManager.getInstance(context);
@@ -357,9 +350,9 @@
         }
         ObjectAnimator anim = createTranslationAnimation(animView, newPos);
         if (useAccelerateInterpolator) {
-            anim.setInterpolator(mFastOutLinearInInterpolator);
+            anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
         } else {
-            anim.setInterpolator(sLinearInterpolator);
+            anim.setInterpolator(Interpolators.LINEAR);
         }
         anim.setDuration(duration);
         if (delay > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
index c9ba885..25f8bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -24,8 +24,8 @@
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
+
+import com.android.systemui.statusbar.Interpolators;
 
 import java.util.ArrayList;
 
@@ -35,7 +35,6 @@
 public class ViewInvertHelper {
 
     private final Paint mDarkPaint = new Paint();
-    private final Interpolator mLinearOutSlowInInterpolator;
     private final ColorMatrix mMatrix = new ColorMatrix();
     private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
     private final long mFadeDuration;
@@ -46,8 +45,6 @@
         addTarget(v);
     }
     public ViewInvertHelper(Context context, long fadeDuration) {
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                android.R.interpolator.linear_out_slow_in);
         mFadeDuration = fadeDuration;
     }
 
@@ -89,7 +86,7 @@
             }
         });
         animator.setDuration(mFadeDuration);
-        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
         animator.setStartDelay(delay);
         animator.start();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index c3a8f2e..92cd027 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.assist;
 
-import com.android.systemui.R;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -35,6 +33,9 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 
+import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
+
 /**
  * Visually discloses that contextual data was provided to an assistant.
  */
@@ -120,13 +121,11 @@
                     R.interpolator.assist_disclosure_trace));
             mAlphaInAnimator = ValueAnimator.ofInt(0, 255).setDuration(ALPHA_IN_ANIMATION_DURATION);
             mAlphaInAnimator.addUpdateListener(this);
-            mAlphaInAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
-                    android.R.interpolator.fast_out_slow_in));
+            mAlphaInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
             mAlphaOutAnimator = ValueAnimator.ofInt(255, 0).setDuration(
                     ALPHA_OUT_ANIMATION_DURATION);
             mAlphaOutAnimator.addUpdateListener(this);
-            mAlphaOutAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
-                    android.R.interpolator.fast_out_linear_in));
+            mAlphaOutAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
             mAnimator = new AnimatorSet();
             mAnimator.play(mAlphaInAnimator).with(mTracingAnimator);
             mAnimator.play(mAlphaInAnimator).before(mAlphaOutAnimator);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
index 67017db..34770c4 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
@@ -20,19 +20,15 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
 
 public class AssistOrbContainer extends FrameLayout {
 
     private static final long EXIT_START_DELAY = 150;
 
-    private final Interpolator mLinearOutSlowInInterpolator;
-    private final Interpolator mFastOutLinearInInterpolator;
-
     private View mScrim;
     private View mNavbarScrim;
     private AssistOrbView mOrb;
@@ -49,10 +45,6 @@
 
     public AssistOrbContainer(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                android.R.interpolator.linear_out_slow_in);
-        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
-                android.R.interpolator.fast_out_slow_in);
     }
 
     @Override
@@ -109,12 +101,12 @@
                         .alpha(1f)
                         .setDuration(300)
                         .setStartDelay(0)
-                        .setInterpolator(mLinearOutSlowInInterpolator);
+                        .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
                 mNavbarScrim.animate()
                         .alpha(1f)
                         .setDuration(300)
                         .setStartDelay(0)
-                        .setInterpolator(mLinearOutSlowInInterpolator);
+                        .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
             }
         });
     }
@@ -132,12 +124,12 @@
                 .alpha(0f)
                 .setDuration(250)
                 .setStartDelay(EXIT_START_DELAY)
-                .setInterpolator(mFastOutLinearInInterpolator);
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         mNavbarScrim.animate()
                 .alpha(0f)
                 .setDuration(250)
                 .setStartDelay(EXIT_START_DELAY)
-                .setInterpolator(mFastOutLinearInInterpolator)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .withEndAction(endRunnable);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
index a3372a8..2d933f6 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
@@ -27,13 +27,13 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewOutlineProvider;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.animation.OvershootInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
 
 public class AssistOrbView extends FrameLayout {
 
@@ -43,8 +43,6 @@
     private final Paint mBackgroundPaint = new Paint();
     private final Rect mCircleRect = new Rect();
     private final Rect mStaticRect = new Rect();
-    private final Interpolator mAppearInterpolator;
-    private final Interpolator mDisappearInterpolator;
     private final Interpolator mOvershootInterpolator = new OvershootInterpolator();
 
     private boolean mClipToOutline;
@@ -117,10 +115,6 @@
                 R.dimen.assist_orb_travel_distance);
         mMaxElevation = context.getResources().getDimensionPixelSize(
                 R.dimen.assist_orb_elevation);
-        mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.linear_out_slow_in);
-        mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.fast_out_linear_in);
         mBackgroundPaint.setAntiAlias(true);
         mBackgroundPaint.setColor(getResources().getColor(R.color.assist_orb_color));
     }
@@ -256,8 +250,8 @@
     }
 
     public void startExitAnimation(long delay) {
-        animateCircleSize(0, 200, delay, mDisappearInterpolator);
-        animateOffset(0, 200, delay, mDisappearInterpolator);
+        animateCircleSize(0, 200, delay, Interpolators.FAST_OUT_LINEAR_IN);
+        animateOffset(0, 200, delay, Interpolators.FAST_OUT_LINEAR_IN);
     }
 
     public void startEnterAnimation() {
@@ -266,7 +260,7 @@
             @Override
             public void run() {
                 animateCircleSize(mCircleMinSize, 300, 0 /* delay */, mOvershootInterpolator);
-                animateOffset(mStaticOffset, 400, 0 /* delay */, mAppearInterpolator);
+                animateOffset(mStaticOffset, 400, 0 /* delay */, Interpolators.LINEAR_OUT_SLOW_IN);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 6e22dde..8ae2d7b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -73,7 +73,7 @@
             mCurrentTiles = tileSpecs;
             final TileGroup group = new TileGroup("com.android.settings", mContext);
             String possible = mContext.getString(R.string.quick_settings_tiles_default)
-                    + ",user,hotspot,inversion";
+                    + ",hotspot,inversion,saver";
             String[] possibleTiles = possible.split(",");
             for (int i = 0; i < possibleTiles.length; i++) {
                 final String spec = possibleTiles[i];
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
new file mode 100644
index 0000000..1aeb0fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.DataSaverController;
+
+public class DataSaverTile extends QSTile<QSTile.BooleanState> implements
+        DataSaverController.Listener{
+
+    private final DataSaverController mDataSaverController;
+
+    public DataSaverTile(Host host) {
+        super(host);
+        mDataSaverController = host.getNetworkController().getDataSaverController();
+    }
+
+    @Override
+    protected BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mDataSaverController.addListener(this);
+        } else {
+            mDataSaverController.remListener(this);
+        }
+    }
+
+    @Override
+    protected void handleClick() {
+        mState.value = !mDataSaverController.isDataSaverEnabled();
+        mDataSaverController.setDataSaverEnabled(mState.value);
+        refreshState(mState.value);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        state.value = arg instanceof Boolean ? (Boolean) arg
+                : mDataSaverController.isDataSaverEnabled();
+        state.label = mContext.getString(R.string.data_saver);
+        state.contentDescription = mContext.getString(state.value ?
+                R.string.accessibility_data_saver_on : R.string.accessibility_data_saver_off);
+        state.icon = ResourceIcon.get(state.value ? R.drawable.ic_data_saver
+                : R.drawable.ic_data_saver_off);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.QS_DATA_SAVER;
+    }
+
+    @Override
+    public void onDataSaverChanged(boolean isDataSaving) {
+        refreshState(isDataSaving);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index 881aa6a..5cd540b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -26,10 +26,6 @@
  */
 public class RecentsDebugFlags implements TunerService.Tunable {
 
-    private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button";
-    private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator";
-    private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging";
-
     public static class Static {
         // Enables debug drawing for the transition thumbnail
         public static final boolean EnableTransitionThumbnailDebugMode = false;
@@ -39,18 +35,23 @@
         public static final boolean DisableBackgroundCache = false;
         // Enables the task affiliations
         public static final boolean EnableAffiliatedTaskGroups = true;
-        // Enables the simulated task affiliations
-        public static final boolean EnableSimulatedTaskGroups = false;
-        // Defines the number of mock task affiliations per group
-        public static final int TaskAffiliationsGroupCount = 12;
+
         // Enables us to create mock recents tasks
-        public static final boolean EnableSystemServicesProxy = false;
+        public static final boolean EnableMockTasks = false;
         // Defines the number of mock recents packages to create
-        public static final int SystemServicesProxyMockPackageCount = 3;
+        public static final int MockTasksPackageCount = 3;
         // Defines the number of mock recents tasks to create
-        public static final int SystemServicesProxyMockTaskCount = 100;
+        public static final int MockTaskCount = 100;
+        // Enables the simulated task affiliations
+        public static final boolean EnableMockTaskGroups = false;
+        // Defines the number of mock task affiliations per group
+        public static final int MockTaskGroupsTaskCount = 12;
     }
 
+    private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button";
+    private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator";
+    private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging";
+
     private boolean mFastToggleRecents;
     private boolean mFastToggleIndicator;
     private boolean mInitialStatePaging;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index f8cbf65..5f11bee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -108,6 +108,10 @@
         public void onActivityPinned() {
         }
 
+        @Override
+        public void onPinnedActivityRestartAttempt() {
+        }
+
         /** Preloads the next task */
         public void run() {
             RecentsConfiguration config = Recents.getConfiguration();
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 3f52ae8..87cfcff 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -151,7 +151,7 @@
         // Resolve the assist intent
         mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
 
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
             // Create a dummy icon
             mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
             mDummyIcon.eraseColor(0xFF999999);
@@ -164,20 +164,20 @@
         if (mAm == null) return null;
 
         // If we are mocking, then create some recent tasks
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
             ArrayList<ActivityManager.RecentTaskInfo> tasks =
                     new ArrayList<ActivityManager.RecentTaskInfo>();
-            int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.SystemServicesProxyMockTaskCount);
+            int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount);
             for (int i = 0; i < count; i++) {
                 // Create a dummy component name
-                int packageIndex = i % RecentsDebugFlags.Static.SystemServicesProxyMockPackageCount;
+                int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount;
                 ComponentName cn = new ComponentName("com.android.test" + packageIndex,
                         "com.android.test" + i + ".Activity");
                 String description = "" + i + " - " +
                         Long.toString(Math.abs(new Random().nextLong()), 36);
                 // Create the recent task info
                 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
-                rti.id = rti.persistentId = i;
+                rti.id = rti.persistentId = rti.affiliatedTaskId = i;
                 rti.baseIntent = new Intent();
                 rti.baseIntent.setComponent(cn);
                 rti.description = description;
@@ -418,7 +418,7 @@
         if (mAm == null) return null;
 
         // If we are mocking, then just return a dummy thumbnail
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
             Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
                     Bitmap.Config.ARGB_8888);
             thumbnail.eraseColor(0xff333333);
@@ -484,7 +484,7 @@
     /** Moves a task to the front with the specified activity options. */
     public void moveTaskToFront(int taskId, ActivityOptions opts) {
         if (mAm == null) return;
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return;
+        if (RecentsDebugFlags.Static.EnableMockTasks) return;
 
         if (opts != null) {
             mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME,
@@ -497,7 +497,7 @@
     /** Removes the task */
     public void removeTask(final int taskId) {
         if (mAm == null) return;
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return;
+        if (RecentsDebugFlags.Static.EnableMockTasks) return;
 
         // Remove the task.
         BackgroundThread.getHandler().post(new Runnable() {
@@ -528,7 +528,7 @@
      */
     public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
         if (mIpm == null) return null;
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return new ActivityInfo();
+        if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
 
         try {
             return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
@@ -545,7 +545,7 @@
      */
     public ActivityInfo getActivityInfo(ComponentName cn) {
         if (mPm == null) return null;
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return new ActivityInfo();
+        if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
 
         try {
             return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
@@ -562,7 +562,7 @@
         if (mPm == null) return null;
 
         // If we are mocking, then return a mock label
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
             return "Recent Task: " + userId;
         }
 
@@ -576,7 +576,7 @@
         if (mPm == null) return null;
 
         // If we are mocking, then return a mock label
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
             return "Recent Task App: " + userId;
         }
 
@@ -588,6 +588,11 @@
      * description joins the app and activity labels.
      */
     public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) {
+        // If we are mocking, then return a mock label
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
+            return "Recent Task Content Description: " + userId;
+        }
+
         String activityLabel = info.loadLabel(mPm).toString();
         String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
         String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
@@ -604,7 +609,7 @@
         if (mPm == null) return null;
 
         // If we are mocking, then return a mock label
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
             return new ColorDrawable(0xFF666666);
         }
 
@@ -620,7 +625,7 @@
         if (mPm == null) return null;
 
         // If we are mocking, then return a mock label
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
             return new ColorDrawable(0xFF666666);
         }
 
@@ -635,7 +640,7 @@
             int userId, Resources res) {
 
         // If we are mocking, then return a mock label
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
             return new ColorDrawable(0xFF666666);
         }
 
@@ -673,7 +678,7 @@
     /** Returns the package name of the home activity. */
     public String getHomeActivityPackageName() {
         if (mPm == null) return null;
-        if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return null;
+        if (RecentsDebugFlags.Static.EnableMockTasks) return null;
 
         ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
         ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 9cdd703..d15828a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -22,6 +22,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.os.Debug;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArraySet;
@@ -31,6 +32,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import java.util.ArrayList;
@@ -130,6 +132,9 @@
                 R.string.accessibility_recents_item_will_be_dismissed);
         long lastStackActiveTime = Prefs.getLong(mContext,
                 Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
+        if (RecentsDebugFlags.Static.EnableMockTasks) {
+            lastStackActiveTime = 0;
+        }
         long newLastStackActiveTime = -1;
         int taskCount = mRawTasks.size();
         for (int i = 0; i < taskCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index de1daa8..66eeac6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -776,7 +776,7 @@
      * Temporary: This method will simulate affiliation groups by
      */
     public void createAffiliatedGroupings(Context context) {
-        if (RecentsDebugFlags.Static.EnableSimulatedTaskGroups) {
+        if (RecentsDebugFlags.Static.EnableMockTaskGroups) {
             ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
             // Sort all tasks by increasing firstActiveTime of the task
             ArrayList<Task> tasks = mStackTaskList.getTasks();
@@ -792,7 +792,7 @@
             String prevPackage = "";
             int prevAffiliation = -1;
             Random r = new Random();
-            int groupCountDown = RecentsDebugFlags.Static.TaskAffiliationsGroupCount;
+            int groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
             for (int i = 0; i < taskCount; i++) {
                 Task t = tasks.get(i);
                 String packageName = t.key.getComponent().getPackageName();
@@ -807,7 +807,7 @@
                     addGroup(group);
                     prevAffiliation = affiliation;
                     prevPackage = packageName;
-                    groupCountDown = RecentsDebugFlags.Static.TaskAffiliationsGroupCount;
+                    groupCountDown = RecentsDebugFlags.Static.MockTaskGroupsTaskCount;
                 }
                 group.addTask(t);
                 taskMap.put(t.key, t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
new file mode 100644
index 0000000..9f2b00a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * This is an optimized FrameLayout whose layout is completely directed by its parent, and as a
+ * result, does not propagate <code>requestLayout()</code> up the view hierarchy. Instead, it will
+ * relayout its children with the last known layout bounds when a layout is requested from a child
+ * view.
+ */
+public class FixedSizeFrameLayout extends FrameLayout {
+
+    private final Rect mLayoutBounds = new Rect();
+
+    public FixedSizeFrameLayout(Context context) {
+        super(context);
+    }
+
+    public FixedSizeFrameLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        measureContents(MeasureSpec.getSize(widthMeasureSpec),
+                MeasureSpec.getSize(heightMeasureSpec));
+    }
+
+    @Override
+    protected final void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        mLayoutBounds.set(left, top, right, bottom);
+        layoutContents(mLayoutBounds, changed);
+    }
+
+    @Override
+    public final void requestLayout() {
+        // The base ViewGroup constructor attempts to call requestLayout() before this class's
+        // members are initialized so we should just propagate in that case
+        if (mLayoutBounds == null || mLayoutBounds.isEmpty()) {
+            super.requestLayout();
+        } else {
+            // If we are already laid out, then just reuse the same bounds to layout the children
+            // (but not itself)
+            // TODO: Investigate whether we should coalesce these to the next frame if needed
+            measureContents(getMeasuredWidth(), getMeasuredHeight());
+            layoutContents(mLayoutBounds, false);
+        }
+    }
+
+    /**
+     * Measures the contents of this fixed layout.
+     */
+    protected void measureContents(int width, int height) {
+        super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+                MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+    }
+
+    /**
+     * Lays out the contents of this fixed layout.
+     */
+    protected void layoutContents(Rect bounds, boolean changed) {
+        super.onLayout(changed, bounds.left, bounds.top, bounds.right, bounds.bottom);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
index 3f5d0a8..f5ab01f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
@@ -23,13 +23,13 @@
 import android.widget.ImageView;
 
 /**
- * This is an optimized ImageView that does not trigger a requestLayout() or invalidate() when
- * setting the image to Null.
+ * This is an optimized ImageView that does not trigger a <code>requestLayout()</code> or
+ * <code>invalidate()</code> when setting the image to <code>null</code>.
  */
 public class FixedSizeImageView extends ImageView {
 
-    boolean mAllowRelayout = true;
-    boolean mAllowInvalidate = true;
+    private boolean mAllowRelayout = true;
+    private boolean mAllowInvalidate = true;
 
     public FixedSizeImageView(Context context) {
         this(context, null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fe9c68e..ccc8581 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1375,11 +1375,6 @@
         // Report that this tasks's data is no longer being used
         Recents.getTaskLoader().unloadTaskData(task);
 
-        // Detach the view from the hierarchy
-        detachViewFromParent(tv);
-        // Update the task views list after removing the task view
-        updateTaskViewsList();
-
         // Reset the view properties and view state
         tv.resetViewProperties();
         tv.setFocusedState(false, false /* requestViewFocus */);
@@ -1387,19 +1382,15 @@
         if (mScreenPinningEnabled) {
             tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
         }
+
+        // Detach the view from the hierarchy
+        detachViewFromParent(tv);
+        // Update the task views list after removing the task view
+        updateTaskViewsList();
     }
 
     @Override
     public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
-        // Rebind the task and request that this task's data be filled into the TaskView
-        tv.onTaskBound(task);
-
-        // Load the task data
-        Recents.getTaskLoader().loadTaskData(task);
-
-        // If the doze trigger has already fired, then update the state for this task view
-        tv.setNoUserInteractionState();
-
         // Find the index where this task should be placed in the stack
         int taskIndex = mStack.indexOfStackTask(task);
         int insertIndex = findTaskViewInsertIndex(task, taskIndex);
@@ -1413,6 +1404,15 @@
         // Update the task views list after adding the new task view
         updateTaskViewsList();
 
+        // Rebind the task and request that this task's data be filled into the TaskView
+        tv.onTaskBound(task);
+
+        // Load the task data
+        Recents.getTaskLoader().loadTaskData(task);
+
+        // If the doze trigger has already fired, then update the state for this task view
+        tv.setNoUserInteractionState();
+
         // Set the new state for this view, including the callbacks and view clipping
         tv.setCallbacks(this);
         tv.setTouchEnabled(true);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 32bebb3..5a4064a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -17,7 +17,6 @@
 package com.android.systemui.recents.views;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
@@ -40,7 +39,6 @@
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsActivity;
@@ -62,8 +60,13 @@
 
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
-/* A task view */
-public class TaskView extends FrameLayout implements Task.TaskCallbacks,
+/**
+ * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed
+ * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down
+ * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself
+ * with the previous bounds if any child requests layout).
+ */
+public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks,
         TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener {
 
     /** The TaskView callbacks */
@@ -219,33 +222,20 @@
         return super.onInterceptTouchEvent(ev);
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
 
+    @Override
+    protected void measureContents(int width, int height) {
         int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
         int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
-        int taskBarHeight = getResources().getDimensionPixelSize(R.dimen.recents_task_bar_height);
 
         // Measure the content
         mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
 
-        // Measure the bar view, and action button
-        mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(taskBarHeight, MeasureSpec.EXACTLY));
-        mActionButtonView.measure(
-                MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST),
-                MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST));
-        // Measure the thumbnail to be square
-        mThumbnailView.measure(
-                MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
+        // Optimization: Prevent overdraw of the thumbnail under the header view
         mThumbnailView.updateClipToTaskBar(mHeaderView);
 
         setMeasuredDimension(width, height);
-        invalidateOutline();
     }
 
     void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 827ee40..cb108da 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -245,11 +245,7 @@
      * to match the frame changes.
      */
     public void onTaskViewSizeChanged(int width, int height) {
-        // Return early if the bounds have not changed
-        if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
-            return;
-        }
-
+        // TODO: Optimize this path
         mTaskViewRect.set(0, 0, width, height);
         boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
         boolean isFreeformTask = (mTask != null) && mTask.isFreeformTask();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 39d0604..de96d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -190,7 +190,6 @@
             if (!mInvisible) {
                 updateThumbnailPaintFilter();
             }
-            invalidate();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 9e83dcf..08793e8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -44,7 +44,6 @@
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
-import android.widget.ImageButton;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DockedDividerUtils;
@@ -72,6 +71,18 @@
     private static final float DIM_START_FRACTION = 0.5f;
     private static final float DIM_DAMP_FACTOR = 1.7f;
 
+    /**
+     * Fraction of the divider position between two snap targets to switch to the full-screen
+     * target.
+     */
+    private static final float SWITCH_FULLSCREEN_FRACTION = 0.12f;
+
+    /**
+     * Fraction of the divider position between two snap targets to switch to the larger target
+     * for the bottom/right app layout.
+     */
+    private static final float BOTTOM_RIGHT_SWITCH_BIGGER_FRACTION = 0.2f;
+
     private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
             new PathInterpolator(0.5f, 1f, 0.5f, 1f);
 
@@ -404,6 +415,12 @@
                     restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget);
             int taskPositionOther =
                     restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
+
+            taskPositionDocked = minimizeHoles(position, taskPositionDocked, mDockSide,
+                    taskSnapTarget);
+            taskPositionOther = minimizeHoles(position, taskPositionOther, dockSideInverted,
+                    taskSnapTarget);
+
             calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
             calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
             alignTopLeft(mDockedRect, mDockedTaskRect);
@@ -435,6 +452,51 @@
     }
 
     /**
+     * Given the current split position and the task position calculated by dragging, this
+     * method calculates a "better" task position in a sense so holes get smaller while dragging.
+     *
+     * @return the new task position
+     */
+    private int minimizeHoles(int position, int taskPosition, int dockSide,
+            SnapTarget taskSnapTarget) {
+        if (dockSideTopLeft(dockSide)) {
+            if (position > taskPosition) {
+                SnapTarget nextTarget = mSnapAlgorithm.getNextTarget(taskSnapTarget);
+
+                // If the next target is the dismiss end target, switch earlier to make the hole
+                // smaller.
+                if (nextTarget != taskSnapTarget
+                        && nextTarget == mSnapAlgorithm.getDismissEndTarget()) {
+                    float t = (float) (position - taskPosition)
+                            / (nextTarget.position - taskPosition);
+                    if (t > SWITCH_FULLSCREEN_FRACTION) {
+                        return nextTarget.position;
+                    }
+                }
+            }
+        } else if (dockSideBottomRight(dockSide)) {
+            if (position < taskPosition) {
+                SnapTarget previousTarget = mSnapAlgorithm.getPreviousTarget(taskSnapTarget);
+                if (previousTarget != taskSnapTarget) {
+                    float t = (float) (taskPosition - position)
+                            / (taskPosition - previousTarget.position);
+
+                    // In general, switch a bit earlier (at 20% instead of 50%), but if we are
+                    // dismissing the top, switch really early.
+                    float threshold = previousTarget == mSnapAlgorithm.getDismissStartTarget()
+                            ? SWITCH_FULLSCREEN_FRACTION
+                            : BOTTOM_RIGHT_SWITCH_BIGGER_FRACTION;
+                    if (t > threshold) {
+                        return previousTarget.position;
+                    }
+
+                }
+            }
+        }
+        return taskPosition;
+    }
+
+    /**
      * When the snap target is dismissing one side, make sure that the dismissing side doesn't get
      * 0 size.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 38d24ce..01bfcea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.animation.TimeAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -28,13 +29,12 @@
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
 import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -107,11 +107,8 @@
 
     private OnActivatedListener mOnActivatedListener;
 
-    private final Interpolator mLinearOutSlowInInterpolator;
-    protected final Interpolator mFastOutSlowInInterpolator;
     private final Interpolator mSlowOutFastInInterpolator;
     private final Interpolator mSlowOutLinearInInterpolator;
-    private final Interpolator mLinearInterpolator;
     private Interpolator mCurrentAppearInterpolator;
     private Interpolator mCurrentAlphaInterpolator;
 
@@ -132,16 +129,37 @@
     private FalsingManager mFalsingManager;
     private boolean mTrackTouch;
 
+    private float mNormalBackgroundVisibilityAmount;
+    private ValueAnimator mFadeInFromDarkAnimator;
+    private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
+            = new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha());
+        }
+    };
+    private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            mFadeInFromDarkAnimator = null;
+            updateOutlineAlpha();
+        }
+    };
+    private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
+            = new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            updateOutlineAlpha();
+        }
+    };
+    private float mShadowAlpha = 1.0f;
+
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-        mFastOutSlowInInterpolator =
-                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
         mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
-        mLinearOutSlowInInterpolator =
-                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
         mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
-        mLinearInterpolator = new LinearInterpolator();
         setClipChildren(false);
         setClipToPadding(false);
         mLegacyColor = context.getColor(R.color.notification_legacy_background_color);
@@ -166,6 +184,7 @@
         mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
         updateBackground();
         updateBackgroundTint();
+        updateOutlineAlpha();
     }
 
     private final Runnable mTapTimeoutRunnable = new Runnable() {
@@ -272,7 +291,7 @@
         }
     }
 
-    private void startActivateAnimation(boolean reverse) {
+    private void startActivateAnimation(final boolean reverse) {
         if (!isAttachedToWindow()) {
             return;
         }
@@ -291,8 +310,8 @@
         Interpolator interpolator;
         Interpolator alphaInterpolator;
         if (!reverse) {
-            interpolator = mLinearOutSlowInInterpolator;
-            alphaInterpolator = mLinearOutSlowInInterpolator;
+            interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
+            alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
         } else {
             interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
             alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
@@ -317,6 +336,16 @@
         mBackgroundNormal.animate()
                 .alpha(reverse ? 0f : 1f)
                 .setInterpolator(alphaInterpolator)
+                .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        float animatedFraction = animation.getAnimatedFraction();
+                        if (reverse) {
+                            animatedFraction = 1.0f - animatedFraction;
+                        }
+                        setNormalBackgroundVisibilityAmount(animatedFraction);
+                    }
+                })
                 .setDuration(ACTIVATE_ANIMATION_LENGTH);
     }
 
@@ -377,8 +406,27 @@
         } else {
             updateBackground();
         }
-        setOutlineAlpha(dark ? 0f : 1f);
-     }
+        updateOutlineAlpha();
+    }
+
+    private void updateOutlineAlpha() {
+        if (mDark) {
+            setOutlineAlpha(0f);
+            return;
+        }
+        float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
+        alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
+        alpha *= mShadowAlpha;
+        if (mFadeInFromDarkAnimator != null) {
+            alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
+        }
+        setOutlineAlpha(alpha);
+    }
+
+    public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
+        mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
+        updateOutlineAlpha();
+    }
 
     public void setShowingLegacyBackground(boolean showing) {
         mShowingLegacyBackground = showing;
@@ -431,7 +479,7 @@
                 .scaleY(1f)
                 .setDuration(DARK_ANIMATION_LENGTH)
                 .setStartDelay(delay)
-                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
                 .setListener(new AnimatorListenerAdapter() {
                     @Override
                     public void onAnimationCancel(Animator animation) {
@@ -441,7 +489,15 @@
                         background.setAlpha(1f);
                     }
                 })
+                .setUpdateListener(mBackgroundVisibilityUpdater)
                 .start();
+        mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
+        mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
+        mFadeInFromDarkAnimator.setStartDelay(delay);
+        mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
+        mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
+        mFadeInFromDarkAnimator.start();
     }
 
     /**
@@ -474,7 +530,7 @@
         mBackgroundNormal.setAlpha(startAlpha);
         mBackgroundAnimator =
                 ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
-        mBackgroundAnimator.setInterpolator(mFastOutSlowInInterpolator);
+        mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         mBackgroundAnimator.setDuration(duration);
         mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -487,6 +543,7 @@
                 mBackgroundAnimator = null;
             }
         });
+        mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
         mBackgroundAnimator.start();
     }
 
@@ -504,6 +561,8 @@
             mBackgroundNormal.setAlpha(1f);
             removeCallbacks(mTapTimeoutRunnable);
         }
+        setNormalBackgroundVisibilityAmount(
+                mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
     }
 
     protected boolean shouldHideBackground() {
@@ -577,16 +636,16 @@
         float targetValue;
         if (isAppearing) {
             mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
-            mCurrentAlphaInterpolator = mLinearOutSlowInInterpolator;
+            mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
             targetValue = 1.0f;
         } else {
-            mCurrentAppearInterpolator = mFastOutSlowInInterpolator;
+            mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
             mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
             targetValue = 0.0f;
         }
         mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
                 targetValue);
-        mAppearAnimator.setInterpolator(mLinearInterpolator);
+        mAppearAnimator.setInterpolator(Interpolators.LINEAR);
         mAppearAnimator.setDuration(
                 (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
         mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -772,6 +831,19 @@
         return getBgColor() == otherView.getBgColor();
     }
 
+    @Override
+    public float getShadowAlpha() {
+        return mShadowAlpha;
+    }
+
+    @Override
+    public void setShadowAlpha(float shadowAlpha) {
+        if (shadowAlpha != mShadowAlpha) {
+            mShadowAlpha = shadowAlpha;
+            updateOutlineAlpha();
+        }
+    }
+
     public interface OnActivatedListener {
         void onActivated(ActivatableNotificationView view);
         void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2592486..efa56bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -186,7 +186,7 @@
 
     protected DevicePolicyManager mDevicePolicyManager;
     protected IDreamManager mDreamManager;
-    PowerManager mPowerManager;
+    protected PowerManager mPowerManager;
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     // public mode, private notifications, etc
@@ -219,8 +219,6 @@
     // which notification is currently being longpress-examined by the user
     private NotificationGuts mNotificationGutsExposed;
 
-    private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
-
     private KeyboardShortcuts mKeyboardShortcuts;
 
     /**
@@ -638,11 +636,6 @@
 
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
 
-        mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.linear_out_slow_in);
-        mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.fast_out_linear_in);
-
         // Connect in to the status bar manager service
         mCommandQueue = new CommandQueue(this);
 
@@ -1016,7 +1009,7 @@
                         final Animator a
                                 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
                         a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                        a.setInterpolator(mLinearOutSlowIn);
+                        a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
                         a.start();
                         guts.setExposed(true);
                         mStackScroller.onHeightChanged(null, true /* needsAnimation */);
@@ -1048,7 +1041,7 @@
             final Animator a = ViewAnimationUtils.createCircularReveal(v,
                     x, y, r, 0);
             a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            a.setInterpolator(mFastOutLinearIn);
+            a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
             a.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index f71f092..24cd948 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -31,7 +31,7 @@
         view.animate()
                 .alpha(0f)
                 .setDuration(ANIMATION_DURATION_LENGTH)
-                .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                .setInterpolator(Interpolators.ALPHA_OUT)
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
@@ -56,7 +56,7 @@
         view.animate()
                 .alpha(1f)
                 .setDuration(ANIMATION_DURATION_LENGTH)
-                .setInterpolator(PhoneStatusBar.ALPHA_IN)
+                .setInterpolator(Interpolators.ALPHA_IN)
                 .withEndAction(null);
         if (view.hasOverlappingRendering()) {
             view.animate().withLayer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 8570198..5c83f5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -24,8 +24,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
+
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
@@ -53,7 +52,6 @@
     private final int[] mTemp2 = new int[2];
     private boolean mDraggedFarEnough;
     private ExpandableView mStartingChild;
-    private Interpolator mInterpolator;
     private float mLastHeight;
     private FalsingManager mFalsingManager;
 
@@ -61,8 +59,6 @@
             DragDownCallback dragDownCallback) {
         mMinDragDistance = context.getResources().getDimensionPixelSize(
                 R.dimen.keyguard_drag_down_min_distance);
-        mInterpolator =
-                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mCallback = callback;
         mDragDownCallback = dragDownCallback;
@@ -187,7 +183,7 @@
         }
         ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight",
                 child.getActualHeight(), child.getMinHeight());
-        anim.setInterpolator(mInterpolator);
+        anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -200,7 +196,7 @@
 
     private void cancelExpansion() {
         ValueAnimator anim = ValueAnimator.ofFloat(mLastHeight, 0);
-        anim.setInterpolator(mInterpolator);
+        anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 93be009..6fae3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1126,6 +1126,11 @@
     }
 
     @Override
+    public boolean needsIncreasedPadding() {
+        return mIsSummaryWithChildren && isGroupExpanded();
+    }
+
+    @Override
     protected boolean disallowSingleClick(MotionEvent event) {
         float x = event.getX();
         float y = event.getY();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index a6fc4bb..44c6a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -24,22 +24,17 @@
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
-import com.android.systemui.R;
-
 /**
  * Like {@link ExpandableView}, but setting an outline for the height and clipping.
  */
 public abstract class ExpandableOutlineView extends ExpandableView {
 
     private final Rect mOutlineRect = new Rect();
-    protected final int mRoundedRectCornerRadius;
     private boolean mCustomOutline;
-    private float mOutlineAlpha = 1f;
+    private float mOutlineAlpha = -1f;
 
     public ExpandableOutlineView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mRoundedRectCornerRadius = getResources().getDimensionPixelSize(
-                R.dimen.notification_material_rounded_rect_radius);
         setOutlineProvider(new ViewOutlineProvider() {
             @Override
             public void getOutline(View view, Outline outline) {
@@ -49,7 +44,7 @@
                             getWidth(),
                             Math.max(getActualHeight(), mClipTopAmount));
                 } else {
-                    outline.setRoundRect(mOutlineRect, mRoundedRectCornerRadius);
+                    outline.setRect(mOutlineRect);
                 }
                 outline.setAlpha(mOutlineAlpha);
             }
@@ -69,8 +64,10 @@
     }
 
     protected void setOutlineAlpha(float alpha) {
-        mOutlineAlpha = alpha;
-        invalidateOutline();
+        if (alpha != mOutlineAlpha) {
+            mOutlineAlpha = alpha;
+            invalidateOutline();
+        }
     }
 
     protected void setOutlineRect(RectF rect) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index c190864..a0fb34a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -388,6 +388,17 @@
         return super.hasOverlappingRendering() && getActualHeight() <= getHeight();
     }
 
+    public float getShadowAlpha() {
+        return 0.0f;
+    }
+
+    public void setShadowAlpha(float shadowAlpha) {
+    }
+
+    public boolean needsIncreasedPadding() {
+        return false;
+    }
+
     /**
      * A listener notifying when {@link #getActualHeight} changes.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 0fa088b..c4ffd7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -19,7 +19,6 @@
 import android.animation.Animator;
 import android.content.Context;
 import android.view.ViewPropertyAnimator;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
@@ -41,8 +40,6 @@
     private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1.0f / LINEAR_OUT_SLOW_IN_X2;
 
     private Interpolator mLinearOutSlowIn;
-    private Interpolator mFastOutSlowIn;
-    private Interpolator mFastOutLinearIn;
 
     private float mMinVelocityPxPerSecond;
     private float mMaxLengthSeconds;
@@ -53,10 +50,6 @@
     public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
         mMaxLengthSeconds = maxLengthSeconds;
         mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
-        mFastOutSlowIn
-                = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
-        mFastOutLinearIn
-                = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_linear_in);
         mMinVelocityPxPerSecond
                 = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
         mHighVelocityPxPerSecond
@@ -150,7 +143,7 @@
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            mAnimatorProperties.interpolator = mFastOutSlowIn;
+            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
         }
         mAnimatorProperties.duration = (long) (durationSeconds * 1000);
         return mAnimatorProperties;
@@ -223,7 +216,7 @@
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            mAnimatorProperties.interpolator = mFastOutLinearIn;
+            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
         }
         mAnimatorProperties.duration = (long) (durationSeconds * 1000);
         return mAnimatorProperties;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java b/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java
new file mode 100644
index 0000000..5979468
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Interpolators.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Utility class to receive interpolators from
+ */
+public class Interpolators {
+    public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+    public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+    public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+    public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+    public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+    public static final Interpolator LINEAR = new LinearInterpolator();
+    public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 8058933..841b9d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -33,13 +33,11 @@
 import android.view.RenderNodeAnimator;
 import android.view.View;
 import android.view.ViewAnimationUtils;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.ImageView;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 /**
  * An ImageView which does not have overlapping renderings commands and therefore does not need a
@@ -55,8 +53,6 @@
 
     private final int mMinBackgroundRadius;
     private final Paint mCirclePaint;
-    private final Interpolator mAppearInterpolator;
-    private final Interpolator mDisappearInterpolator;
     private final int mInverseColor;
     private final int mNormalColor;
     private final ArgbEvaluator mColorInterpolator;
@@ -136,10 +132,6 @@
         mInverseColor = 0xff000000;
         mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
                 R.dimen.keyguard_affordance_min_background_radius);
-        mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.linear_out_slow_in);
-        mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.fast_out_linear_in);
         mColorInterpolator = new ArgbEvaluator();
         mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f);
     }
@@ -261,7 +253,7 @@
             RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
                     RenderNodeAnimator.PAINT_ALPHA, 255);
             animator.setTarget(this);
-            animator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+            animator.setInterpolator(Interpolators.ALPHA_IN);
             animator.setDuration(250);
             animator.start();
         }
@@ -282,7 +274,7 @@
         RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
                 RenderNodeAnimator.PAINT_ALPHA, 0);
         animator.setDuration(duration);
-        animator.setInterpolator(PhoneStatusBar.ALPHA_OUT);
+        animator.setInterpolator(Interpolators.ALPHA_OUT);
         animator.setTarget(this);
         animator.start();
     }
@@ -352,8 +344,8 @@
             cancelAnimator(mPreviewClipper);
             ValueAnimator animator = getAnimatorToRadius(circleRadius);
             Interpolator interpolator = circleRadius == 0.0f
-                    ? mDisappearInterpolator
-                    : mAppearInterpolator;
+                    ? Interpolators.FAST_OUT_LINEAR_IN
+                    : Interpolators.LINEAR_OUT_SLOW_IN;
             animator.setInterpolator(interpolator);
             long duration = 250;
             if (!slowAnimation) {
@@ -438,8 +430,8 @@
             animator.addListener(mScaleEndListener);
             if (interpolator == null) {
                 interpolator = imageScale == 0.0f
-                        ? mDisappearInterpolator
-                        : mAppearInterpolator;
+                        ? Interpolators.FAST_OUT_LINEAR_IN
+                        : Interpolators.LINEAR_OUT_SLOW_IN;
             }
             animator.setInterpolator(interpolator);
             if (duration == -1) {
@@ -501,8 +493,8 @@
             animator.addListener(mAlphaEndListener);
             if (interpolator == null) {
                 interpolator = alpha == 0.0f
-                        ? mDisappearInterpolator
-                        : mAppearInterpolator;
+                        ? Interpolators.FAST_OUT_LINEAR_IN
+                        : Interpolators.LINEAR_OUT_SLOW_IN;
             }
             animator.setInterpolator(interpolator);
             if (duration == -1) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 36cf906..635e66d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -52,8 +52,6 @@
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
 
     private final Rect mClipBounds = new Rect();
-    private final int mRoundRectRadius;
-    private final boolean mRoundRectClippingEnabled;
     private final int mMinContractedHeight;
     private final OnLayoutChangeListener mLayoutUpdater = new OnLayoutChangeListener() {
         @Override
@@ -100,13 +98,6 @@
         }
     };
 
-    private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
-        @Override
-        public void getOutline(View view, Outline outline) {
-            outline.setRoundRect(0, 0, view.getWidth(), mUnrestrictedContentHeight,
-                    mRoundRectRadius);
-        }
-    };
     private OnClickListener mExpandClickListener;
     private boolean mBeforeN;
     private boolean mExpandable;
@@ -116,14 +107,9 @@
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
-        mRoundRectRadius = getResources().getDimensionPixelSize(
-                R.dimen.notification_material_rounded_rect_radius);
-        mRoundRectClippingEnabled = getResources().getBoolean(
-                R.bool.config_notifications_round_rect_clipping);
         mMinContractedHeight = getResources().getDimensionPixelSize(
                 R.dimen.min_notification_layout_height);
         reset(true);
-        setOutlineProvider(mOutlineProvider);
     }
 
     public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
@@ -253,7 +239,6 @@
         mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
         selectLayout(false /* animate */, true /* force */);
         mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
-        updateRoundRectClipping();
     }
 
     public void setExpandedChild(View child) {
@@ -267,7 +252,6 @@
         mExpandedChild.addOnLayoutChangeListener(mLayoutUpdater);
         mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child);
         selectLayout(false /* animate */, true /* force */);
-        updateRoundRectClipping();
     }
 
     public void setHeadsUpChild(View child) {
@@ -281,7 +265,6 @@
         mHeadsUpChild.addOnLayoutChangeListener(mLayoutUpdater);
         mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child);
         selectLayout(false /* animate */, true /* force */);
-        updateRoundRectClipping();
     }
 
     @Override
@@ -344,27 +327,6 @@
         updateClipping();
     }
 
-    private void updateRoundRectClipping() {
-        boolean enabled = needsRoundRectClipping();
-        setClipToOutline(enabled);
-    }
-
-    private boolean needsRoundRectClipping() {
-        if (!mRoundRectClippingEnabled) {
-            return false;
-        }
-        boolean needsForContracted = mContractedChild != null
-                && mContractedChild.getVisibility() == View.VISIBLE
-                && mContractedWrapper.needsRoundRectClipping();
-        boolean needsForExpanded = mExpandedChild != null
-                && mExpandedChild.getVisibility() == View.VISIBLE
-                && mExpandedWrapper.needsRoundRectClipping();
-        boolean needsForHeadsUp = mExpandedChild != null
-                && mExpandedChild.getVisibility() == View.VISIBLE
-                && mExpandedWrapper.needsRoundRectClipping();
-        return needsForContracted || needsForExpanded || needsForHeadsUp;
-    }
-
     private void updateClipping() {
         if (mClipToActualHeight) {
             mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
@@ -412,7 +374,6 @@
             boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
             mSingleLineView.setVisible(singleLineVisible);
         }
-        updateRoundRectClipping();
     }
 
     private void animateToVisibleType(int visibleType) {
@@ -426,7 +387,6 @@
                 hiddenView.setVisible(false);
             }
         });
-        updateRoundRectClipping();
     }
 
     /**
@@ -553,7 +513,6 @@
         if (mHeadsUpChild != null) {
             mHeadsUpWrapper.notifyContentUpdated(entry.notification);
         }
-        updateRoundRectClipping();
     }
 
     private void updateSingleLineView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index e4cd7d9..5abd1d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -206,13 +206,8 @@
     void saveImportance(final StatusBarNotification sbn) {
         int progress = mSeekBar.getProgress();
         try {
-            if (mApplyToTopic.isChecked()) {
-                mINotificationManager.setTopicImportance(sbn.getPackageName(), sbn.getUid(), mTopic,
-                        progress);
-            } else {
-                mINotificationManager.setAppImportance(
-                        sbn.getPackageName(), sbn.getUid(), progress);
-            }
+            mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(),
+                    mApplyToTopic.isChecked() ? mTopic : null, progress);
         } catch (RemoteException e) {
             // :(
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index 682676b..4e3ecb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -22,7 +22,10 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Paint;
 import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -32,11 +35,14 @@
  */
 public class ScrimView extends View
 {
+    private final Paint mPaint = new Paint();
     private int mScrimColor;
     private boolean mIsEmpty = true;
     private boolean mDrawAsSrc;
     private float mViewAlpha = 1.0f;
     private ValueAnimator mAlphaAnimator;
+    private Rect mExcludedRect = new Rect();
+    private boolean mHasExcludedArea;
     private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
             = new ValueAnimator.AnimatorUpdateListener() {
         @Override
@@ -51,6 +57,7 @@
             mAlphaAnimator = null;
         }
     };
+    private Runnable mChangeRunnable;
 
     public ScrimView(Context context) {
         this(context, null);
@@ -72,15 +79,43 @@
     protected void onDraw(Canvas canvas) {
         if (mDrawAsSrc || (!mIsEmpty && mViewAlpha > 0f)) {
             PorterDuff.Mode mode = mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER;
-            int color = mScrimColor;
-            color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color),
-                    Color.green(color), Color.blue(color));
-            canvas.drawColor(color, mode);
+            int color = getScrimColorWithAlpha();
+            if (!mHasExcludedArea) {
+                canvas.drawColor(color, mode);
+            } else {
+                mPaint.setColor(color);
+                if (mExcludedRect.top > 0) {
+                    canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint);
+                }
+                if (mExcludedRect.left > 0) {
+                    canvas.drawRect(0,  mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom,
+                            mPaint);
+                }
+                if (mExcludedRect.right < getWidth()) {
+                    canvas.drawRect(mExcludedRect.right,
+                            mExcludedRect.top,
+                            getWidth(),
+                            mExcludedRect.bottom,
+                            mPaint);
+                }
+                if (mExcludedRect.bottom < getHeight()) {
+                    canvas.drawRect(0,  mExcludedRect.bottom, getWidth(), getHeight(), mPaint);
+                }
+            }
         }
     }
 
+    public int getScrimColorWithAlpha() {
+        int color = mScrimColor;
+        color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color),
+                Color.green(color), Color.blue(color));
+        return color;
+    }
+
     public void setDrawAsSrc(boolean asSrc) {
         mDrawAsSrc = asSrc;
+        mPaint.setXfermode(new PorterDuffXfermode(mDrawAsSrc ? PorterDuff.Mode.SRC
+                : PorterDuff.Mode.SRC_OVER));
         invalidate();
     }
 
@@ -89,6 +124,9 @@
             mIsEmpty = Color.alpha(color) == 0;
             mScrimColor = color;
             invalidate();
+            if (mChangeRunnable != null) {
+                mChangeRunnable.run();
+            }
         }
     }
 
@@ -105,8 +143,13 @@
         if (mAlphaAnimator != null) {
             mAlphaAnimator.cancel();
         }
-        mViewAlpha = alpha;
-        invalidate();
+        if (alpha != mViewAlpha) {
+            mViewAlpha = alpha;
+            invalidate();
+            if (mChangeRunnable != null) {
+                mChangeRunnable.run();
+            }
+        }
     }
 
     public void animateViewAlpha(float alpha, long durationOut, Interpolator interpolator) {
@@ -120,4 +163,23 @@
         mAlphaAnimator.setDuration(durationOut);
         mAlphaAnimator.start();
     }
+
+    public void setExcludedArea(Rect area) {
+        if (area == null) {
+            mHasExcludedArea = false;
+            invalidate();
+            return;
+        }
+        area.left = Math.max(area.left, 0);
+        area.top = Math.max(area.top, 0);
+        area.right = Math.min(area.right, getWidth());
+        area.bottom = Math.min(area.bottom, getHeight());
+        mExcludedRect.set(area);
+        mHasExcludedArea = area.left < area.right && area.top < area.bottom;
+        invalidate();
+    }
+
+    public void setChangeRunnable(Runnable changeRunnable) {
+        mChangeRunnable = changeRunnable;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
deleted file mode 100644
index 1fc8744..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import com.android.systemui.R;
-
-/**
- * The view representing the separation between important and less important notifications
- */
-public class SpeedBumpView extends ExpandableView {
-
-    private final int mSpeedBumpHeight;
-    private AlphaOptimizedView mLine;
-    private boolean mIsVisible = true;
-    private final Interpolator mFastOutSlowInInterpolator;
-
-    public SpeedBumpView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mSpeedBumpHeight = getResources()
-                .getDimensionPixelSize(R.dimen.speed_bump_height);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.fast_out_slow_in);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mLine = (AlphaOptimizedView) findViewById(R.id.speedbump_line);
-    }
-
-    @Override
-    protected int getInitialHeight() {
-        return mSpeedBumpHeight;
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mSpeedBumpHeight;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mLine.setPivotX(mLine.getWidth() / 2);
-        mLine.setPivotY(mLine.getHeight() / 2);
-        setOutlineProvider(null);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        measureChildren(widthMeasureSpec, heightMeasureSpec);
-        int height = mSpeedBumpHeight;
-        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height);
-    }
-
-    @Override
-    public boolean isTransparent() {
-        return true;
-    }
-
-    public void performVisibilityAnimation(boolean nowVisible, long delay) {
-        animateDivider(nowVisible, delay, null /* onFinishedRunnable */);
-    }
-
-    /**
-     * Animate the divider to a new visibility.
-     *
-     * @param nowVisible should it now be visible
-     * @param delay the delay after the animation should start
-     * @param onFinishedRunnable A runnable which should be run when the animation is
-     *        finished.
-     */
-    public void animateDivider(boolean nowVisible, long delay, Runnable onFinishedRunnable) {
-        if (nowVisible != mIsVisible) {
-            // Animate dividers
-            float endValue = nowVisible ? 1.0f : 0.0f;
-            mLine.animate()
-                    .alpha(endValue)
-                    .setStartDelay(delay)
-                    .scaleX(endValue)
-                    .scaleY(endValue)
-                    .setInterpolator(mFastOutSlowInInterpolator)
-                    .withEndAction(onFinishedRunnable);
-            mIsVisible = nowVisible;
-        } else {
-            if (onFinishedRunnable != null) {
-                onFinishedRunnable.run();
-            }
-        }
-    }
-
-    public void setInvisible() {
-        mLine.setAlpha(0.0f);
-        mLine.setScaleX(0.0f);
-        mLine.setScaleY(0.0f);
-        mIsVisible = false;
-    }
-
-    @Override
-    public void performRemoveAnimation(long duration, float translationDirection,
-            Runnable onFinishedRunnable) {
-        // TODO: Use duration
-        performVisibilityAnimation(false, 0 /* delay */);
-    }
-
-    @Override
-    public void performAddAnimation(long delay, long duration) {
-        // TODO: Use duration
-        performVisibilityAnimation(true, delay);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index 2f66c41..c836637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -21,8 +21,6 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
 /**
  * A common base class for all views in the notification stack scroller which don't have a
  * background.
@@ -80,9 +78,9 @@
             float endValue = nowVisible ? 1.0f : 0.0f;
             Interpolator interpolator;
             if (nowVisible) {
-                interpolator = PhoneStatusBar.ALPHA_IN;
+                interpolator = Interpolators.ALPHA_IN;
             } else {
-                interpolator = PhoneStatusBar.ALPHA_OUT;
+                interpolator = Interpolators.ALPHA_OUT;
             }
             mAnimating = true;
             mContent.animate()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index 3e2c4c6..bed64a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -39,6 +39,10 @@
  * the navigation buttons by updating arrays_car.xml appropriately in an overlay.
  */
 class CarNavigationBarController {
+    private static final String EXTRA_FACET_CATEGORIES = "categories";
+    private static final String EXTRA_FACET_PACKAGES = "packages";
+    private static final String EXTRA_FACET_ID = "filter_id";
+    private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
 
     // Each facet of the navigation bar maps to a set of package names or categories defined in
     // arrays_car.xml. Package names for a given facet are delimited by ";"
@@ -64,6 +68,7 @@
     private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>();
 
     private int mCurrentFacetIndex;
+    private String mCurrentPackageName;
 
     public CarNavigationBarController(Context context,
                                       CarNavigationBarView navBar,
@@ -75,6 +80,7 @@
     }
 
     public void taskChanged(String packageName) {
+        mCurrentPackageName = packageName;
         // If the package name belongs to a filter, then highlight appropriate button in
         // the navigation bar.
         if (mFacetPackageMap.containsKey(packageName)) {
@@ -121,7 +127,8 @@
 
                 CarNavigationButton button = createNavButton(icon, i, hasLongpress);
                 mNavButtons.add(button);
-                mNavBar.addButton(button, createNavButton(icon, i, hasLongpress));
+                mNavBar.addButton(button,
+                        createNavButton(icon, i, hasLongpress) /* lightsOutButton */);
 
                 initFacetFilterMaps(i,
                         facetPackageNames.getString(i).split(FACET_FILTER_DEMILITER),
@@ -132,7 +139,7 @@
         }
     }
 
-    private void initFacetFilterMaps(int id, String[] packageNames, String[] categories){
+    private void initFacetFilterMaps(int id, String[] packageNames, String[] categories) {
         mFacetCategories.add(categories);
         for (int i = 0; i < categories.length; i++) {
             mFacetCategoryMap.put(categories[i], id);
@@ -234,7 +241,6 @@
         button.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                setCurrentFacet(id);
                 onFacetClicked(id);
             }
         });
@@ -245,13 +251,13 @@
                 @Override
                 public boolean onLongClick(View v) {
                     onFacetLongClicked(id);
-                    setCurrentFacet(id);
                     return true;
                 }
             });
         } else {
             button.setLongClickable(false);
         }
+
         return button;
     }
 
@@ -262,14 +268,35 @@
     }
 
     private void onFacetClicked(int index) {
-        // TODO: determine what data to pass to the trampoline, so it can start
-        // the default app or the lens picker.
-        startActivity(mIntents.get(index));
+        Intent intent = mIntents.get(index);
+        String packageName = intent.getPackage();
+
+        if (packageName == null) {
+            return;
+        }
+
+        // Don't launch the lens picker if it's already running and the
+        // user clicks the same facet
+        if (packageName.equals(mCurrentPackageName) && index == mCurrentFacetIndex) {
+            return;
+        }
+
+        intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index));
+        intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index));
+        // The facet is identified by the index in which it was added to the nav bar.
+        // This value can be used to determine which facet was selected
+        intent.putExtra(EXTRA_FACET_ID, Integer.toString(index));
+
+        // If the current facet is clicked, we want to launch the picker by default
+        // rather than the "preferred/last run" app.
+        intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex);
+
+        setCurrentFacet(index);
+        startActivity(intent);
     }
 
     private void onFacetLongClicked(int index) {
-        // TODO: determine what data to pass to the trampoline, so it can start
-        // the default app or the lens picker.
+        setCurrentFacet(index);
         startActivity(mLongPressIntents.get(index));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 36b3a8a..504f059 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -18,8 +18,6 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.widget.ImageButton;
-import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.R;
@@ -43,13 +41,11 @@
         super.onFinishInflate();
         mIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_icon);
         mIcon.setClickable(false);
-        mIcon.setScaleType(ImageView.ScaleType.CENTER);
         mIcon.setBackgroundColor(android.R.color.transparent);
         mIcon.setAlpha(UNSELECTED_ALPHA);
 
         mMoreIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_more_icon);
         mMoreIcon.setClickable(false);
-        mMoreIcon.setScaleType(ImageView.ScaleType.CENTER);
         mMoreIcon.setBackgroundColor(android.R.color.transparent);
         mMoreIcon.setVisibility(INVISIBLE);
         mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index a8e09f8..e20936b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -105,6 +105,10 @@
         }
 
         @Override
+        public void onPinnedActivityRestartAttempt() {
+        }
+
+        @Override
         public void onTaskStackChanged() {
             mHandler.removeCallbacks(this);
             mHandler.post(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index fd65aac..97bf4b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -41,9 +41,4 @@
             mInvertHelper.update(dark);
         }
     }
-
-    @Override
-    public boolean needsRoundRectClipping() {
-        return true;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 4fd4cab..f43a5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -38,6 +38,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.ViewTransformationHelper;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
@@ -55,7 +56,6 @@
             0, PorterDuff.Mode.SRC_ATOP);
     private final int mIconDarkAlpha;
     private final int mIconDarkColor = 0xffffffff;
-    protected final Interpolator mLinearOutSlowInInterpolator;
     protected final ViewInvertHelper mInvertHelper;
 
     protected final ViewTransformationHelper mTransformationHelper;
@@ -69,8 +69,6 @@
     protected NotificationHeaderViewWrapper(Context ctx, View view) {
         super(view);
         mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
-                android.R.interpolator.linear_out_slow_in);
         mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
         mTransformationHelper = new ViewTransformationHelper();
         resolveHeaderViews();
@@ -185,7 +183,7 @@
         ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
         animator.addUpdateListener(updateListener);
         animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
-        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
         animator.setStartDelay(delay);
         if (listener != null) {
             animator.addListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index a959e07..3475d13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -26,6 +26,7 @@
 import android.widget.TextView;
 
 import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.ViewTransformationHelper;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -64,7 +65,7 @@
                                             - ownPosition[1]) * 0.33f)
                                     .setDuration(
                                             StackStateAnimator.ANIMATION_DURATION_STANDARD)
-                                    .setInterpolator(TransformState.FAST_OUT_SLOW_IN)
+                                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                                     .withEndAction(new Runnable() {
                                         @Override
                                         public void run() {
@@ -103,7 +104,7 @@
                                     .translationY(0)
                                     .setDuration(
                                             StackStateAnimator.ANIMATION_DURATION_STANDARD)
-                                    .setInterpolator(TransformState.FAST_OUT_SLOW_IN)
+                                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                                     .withEndAction(new Runnable() {
                                         @Override
                                         public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 0ceba78..a1cf07e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -65,14 +65,6 @@
     public void notifyContentUpdated(StatusBarNotification notification) {};
 
     /**
-     * @return true if this template might need to be clipped with a round rect to make it look
-     *         nice, false otherwise
-     */
-    public boolean needsRoundRectClipping() {
-        return false;
-    }
-
-    /**
      * Update the appearance of the expand button.
      *
      * @param expandable should this view be expandable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 388ba0e..3e1c40a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -22,8 +22,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
@@ -31,6 +29,7 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 /**
@@ -44,7 +43,6 @@
     private static final int CLIP_CLIPPING_SET = R.id.clip_children_set_tag;
     private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag;
     private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag;
-    public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
     private static Pools.SimplePool<TransformState> sInstancePool = new Pools.SimplePool<>(40);
 
     protected View mTransformedView;
@@ -110,7 +108,7 @@
             }
         }
         transformedView.animate()
-                .setInterpolator(TransformState.FAST_OUT_SLOW_IN)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
                 .withEndAction(new Runnable() {
                     @Override
@@ -168,7 +166,7 @@
                     .translationY(otherStablePosition[1] - ownPosition[1]);
         }
         transformedView.animate()
-                .setInterpolator(TransformState.FAST_OUT_SLOW_IN)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
                 .withEndAction(new Runnable() {
                     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index a3f404a..37e5558 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -32,6 +32,7 @@
 import android.view.animation.LinearInterpolator;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
 
 public class BarTransitions {
     private static final boolean DEBUG = false;
@@ -124,7 +125,6 @@
         private final int mTransparent;
         private final int mWarning;
         private final Drawable mGradient;
-        private final TimeInterpolator mInterpolator;
 
         private int mMode = -1;
         private boolean mAnimating;
@@ -152,7 +152,6 @@
                 mWarning = context.getColor(com.android.internal.R.color.battery_saver_mode_color);
             }
             mGradient = context.getDrawable(gradientResourceId);
-            mInterpolator = new LinearInterpolator();
         }
 
         @Override
@@ -222,7 +221,8 @@
                     mGradientAlpha = targetGradientAlpha;
                 } else {
                     final float t = (now - mStartTime) / (float)(mEndTime - mStartTime);
-                    final float v = Math.max(0, Math.min(mInterpolator.getInterpolation(t), 1));
+                    final float v = Math.max(0, Math.min(
+                            Interpolators.LINEAR.getInterpolation(t), 1));
                     mGradientAlpha = (int)(v * targetGradientAlpha + mGradientAlphaStart * (1 - v));
                     mColor = Color.argb(
                           (int)(v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1 - v)),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 3ff69c9..b5dba18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -23,11 +23,11 @@
 import android.content.Context;
 import android.os.Handler;
 import android.util.Log;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
+import com.android.systemui.statusbar.Interpolators;
 
 /**
  * Controller which handles all the doze animations of the scrims.
@@ -37,10 +37,6 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final DozeParameters mDozeParameters;
-    private final Interpolator mPulseInInterpolator = PhoneStatusBar.ALPHA_OUT;
-    private final Interpolator mPulseInInterpolatorPickup;
-    private final Interpolator mPulseOutInterpolator = PhoneStatusBar.ALPHA_IN;
-    private final Interpolator mDozeAnimationInterpolator;
     private final Handler mHandler = new Handler();
     private final ScrimController mScrimController;
 
@@ -55,8 +51,6 @@
     public DozeScrimController(ScrimController scrimController, Context context) {
         mScrimController = scrimController;
         mDozeParameters = new DozeParameters(context);
-        mDozeAnimationInterpolator = mPulseInInterpolatorPickup =
-                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
     }
 
     public void setDozing(boolean dozing, boolean animate) {
@@ -70,9 +64,11 @@
             cancelPulsing();
             if (animate) {
                 startScrimAnimation(false /* inFront */, 0f /* target */,
-                        NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator);
+                        NotificationPanelView.DOZE_ANIMATION_DURATION,
+                        Interpolators.LINEAR_OUT_SLOW_IN);
                 startScrimAnimation(true /* inFront */, 0f /* target */,
-                        NotificationPanelView.DOZE_ANIMATION_DURATION, mDozeAnimationInterpolator);
+                        NotificationPanelView.DOZE_ANIMATION_DURATION,
+                        Interpolators.LINEAR_OUT_SLOW_IN);
             } else {
                 abortAnimations();
                 mScrimController.setDozeBehindAlpha(0f);
@@ -116,7 +112,7 @@
             final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
             startScrimAnimation(true /* inFront */, 0f,
                     mDozeParameters.getPulseInDuration(pickup),
-                    pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator,
+                    pickup ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
                     mPulseInFinished);
         }
     }
@@ -266,7 +262,7 @@
             if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
             if (!mDozing) return;
             startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(),
-                    mPulseOutInterpolator, mPulseOutFinished);
+                    Interpolators.ALPHA_IN, mPulseOutFinished);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
deleted file mode 100644
index d2bec7c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
+++ /dev/null
@@ -1,113 +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.statusbar.phone;
-
-import android.app.AppGlobals;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import android.util.Slog;
-import android.widget.ImageView;
-
-/**
- * Retrieves the icon for an activity and sets it as the Drawable on an ImageView. The ImageView
- * is hidden if the activity isn't recognized or if there is no icon.
- */
-class GetActivityIconTask extends AsyncTask<AppButtonData, Void, Drawable> {
-    private final static String TAG = "GetActivityIconTask";
-
-    private final PackageManager mPackageManager;
-
-    // The ImageView that will receive the icon.
-    private final ImageView mImageView;
-
-    public GetActivityIconTask(PackageManager packageManager, ImageView imageView) {
-        mPackageManager = packageManager;
-        mImageView = imageView;
-    }
-
-    @Override
-    protected Drawable doInBackground(AppButtonData... params) {
-        if (params.length != 1) {
-            throw new IllegalArgumentException("Expected one parameter");
-        }
-        AppButtonData buttonData = params[0];
-        AppInfo appInfo = buttonData.appInfo;
-        try {
-            IPackageManager mPM = AppGlobals.getPackageManager();
-            ActivityInfo ai = mPM.getActivityInfo(
-                    appInfo.getComponentName(),
-                    0,
-                    appInfo.getUser().getIdentifier());
-
-            if (ai == null) {
-                Slog.w(TAG, "Icon not found for " + appInfo);
-                return null;
-            }
-
-            Drawable unbadgedIcon = ai.loadIcon(mPackageManager);
-            Drawable badgedIcon =
-                    mPackageManager.getUserBadgedIcon(unbadgedIcon, appInfo.getUser());
-
-            if (NavigationBarApps.DEBUG) {
-                // Draw pinned indicator and number of running tasks.
-                Bitmap bitmap = Bitmap.createBitmap(
-                        badgedIcon.getIntrinsicWidth(),
-                        badgedIcon.getIntrinsicHeight(),
-                        Bitmap.Config.ARGB_8888);
-                Canvas canvas = new Canvas(bitmap);
-                badgedIcon.setBounds(
-                        0, 0, badgedIcon.getIntrinsicWidth(), badgedIcon.getIntrinsicHeight());
-                badgedIcon.draw(canvas);
-                Paint paint = new Paint();
-                paint.setStyle(Paint.Style.FILL);
-                if (buttonData.pinned) {
-                    paint.setColor(Color.WHITE);
-                    canvas.drawCircle(10, 10, 10, paint);
-                }
-                if (buttonData.tasks != null && buttonData.tasks.size() > 0) {
-                    paint.setColor(Color.BLACK);
-                    canvas.drawCircle(60, 30, 30, paint);
-                    paint.setColor(Color.WHITE);
-                    paint.setTextSize(50);
-                    paint.setTypeface(Typeface.create("sans-serif", Typeface.BOLD));
-                    canvas.drawText(Integer.toString(buttonData.tasks.size()), 50, 50, paint);
-                }
-                badgedIcon = new BitmapDrawable(null, bitmap);
-            }
-
-            return  badgedIcon;
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Icon not found for " + appInfo, e);
-            return null;
-        }
-    }
-
-    @Override
-    protected void onPostExecute(Drawable icon) {
-        mImageView.setImageDrawable(icon);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 26abc48..e7064e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -43,7 +43,6 @@
     private boolean mCollapseSnoozes;
     private NotificationPanelView mPanel;
     private ExpandableNotificationRow mPickedChild;
-    private final int mNotificationsTopPadding;
 
     public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
             NotificationStackScrollLayout stackScroller,
@@ -54,8 +53,6 @@
         Context context = stackScroller.getContext();
         final ViewConfiguration configuration = ViewConfiguration.get(context);
         mTouchSlop = configuration.getScaledTouchSlop();
-        mNotificationsTopPadding = context.getResources()
-                .getDimensionPixelSize(R.dimen.notifications_top_padding);
     }
 
     public boolean isTrackingHeadsUp() {
@@ -80,10 +77,6 @@
                 mInitialTouchX = x;
                 setTrackingHeadsUp(false);
                 ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
-                if (child == null && y < mNotificationsTopPadding) {
-                    // We should also allow drags from the margin above the heads up
-                    child = mStackScroller.getChildAtRawPosition(x, y + mNotificationsTopPadding);
-                }
                 mTouchingHeadsUpView = false;
                 if (child instanceof ExpandableNotificationRow) {
                     mPickedChild = (ExpandableNotificationRow) child;
@@ -113,8 +106,7 @@
                     mHeadsUpManager.unpinAll();
                     mPanel.setPanelScrimMinFraction((float) expandedHeight
                             / mPanel.getMaxPanelHeight());
-                    mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight
-                            + mNotificationsTopPadding);
+                    mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
                     mPanel.clearNotificattonEffects();
                     return true;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 41adeb5..c220efe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -24,12 +24,11 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 
 /**
@@ -60,8 +59,6 @@
     private KeyguardAffordanceView mLeftIcon;
     private KeyguardAffordanceView mCenterIcon;
     private KeyguardAffordanceView mRightIcon;
-    private Interpolator mAppearInterpolator;
-    private Interpolator mDisappearInterpolator;
     private Animator mSwipeAnimator;
     private FalsingManager mFalsingManager;
     private int mMinBackgroundRadius;
@@ -107,10 +104,6 @@
         mHintGrowAmount =
                 mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways);
         mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
-        mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.linear_out_slow_in);
-        mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.fast_out_linear_in);
         mFalsingManager = FalsingManager.getInstance(mContext);
     }
 
@@ -272,7 +265,7 @@
                 }
             }
         });
-        animator.setInterpolator(mAppearInterpolator);
+        animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
         animator.setDuration(HINT_PHASE1_DURATION);
         animator.start();
         mSwipeAnimator = animator;
@@ -292,7 +285,7 @@
                 onFinishedListener.run();
             }
         });
-        animator.setInterpolator(mDisappearInterpolator);
+        animator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
         animator.setDuration(HINT_PHASE2_DURATION);
         animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION);
         animator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 14176a6..94d3829 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -58,6 +58,7 @@
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -111,7 +112,6 @@
     private AccessibilityController mAccessibilityController;
     private PhoneStatusBar mPhoneStatusBar;
 
-    private final Interpolator mLinearOutSlowInInterpolator;
     private boolean mUserSetupComplete;
     private boolean mPrewarmBound;
     private Messenger mPrewarmMessenger;
@@ -146,8 +146,6 @@
     public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mLinearOutSlowInInterpolator =
-                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
     }
 
     private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
@@ -600,7 +598,7 @@
         mIndicationText.setAlpha(0f);
         mIndicationText.animate()
                 .alpha(1f)
-                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
                 .setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
     }
 
@@ -610,7 +608,7 @@
         element.animate()
                 .alpha(1f)
                 .translationY(0f)
-                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
                 .setStartDelay(delay)
                 .setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index f41e47b..e67aa84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -23,14 +23,13 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewTreeObserver;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -58,7 +57,6 @@
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
 
     private int mSystemIconsSwitcherHiddenExpandedMargin;
-    private Interpolator mFastOutSlowInInterpolator;
 
     public KeyguardStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -73,8 +71,6 @@
         mBatteryLevel = (TextView) findViewById(R.id.battery_level);
         mCarrierLabel = (TextView) findViewById(R.id.keyguard_carrier_text);
         loadDimens();
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.fast_out_slow_in);
         updateUserSwitcher();
     }
 
@@ -199,7 +195,7 @@
                         .translationX(0)
                         .setDuration(400)
                         .setStartDelay(userSwitcherHiding ? 300 : 0)
-                        .setInterpolator(mFastOutSlowInInterpolator)
+                        .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                         .start();
                 if (userSwitcherHiding) {
                     getOverlay().add(mMultiUserSwitch);
@@ -207,7 +203,7 @@
                             .alpha(0f)
                             .setDuration(300)
                             .setStartDelay(0)
-                            .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                            .setInterpolator(Interpolators.ALPHA_OUT)
                             .withEndAction(new Runnable() {
                                 @Override
                                 public void run() {
@@ -223,7 +219,7 @@
                             .alpha(1f)
                             .setDuration(300)
                             .setStartDelay(200)
-                            .setInterpolator(PhoneStatusBar.ALPHA_IN);
+                            .setInterpolator(Interpolators.ALPHA_IN);
                 }
                 return true;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 71267cd..8717a15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -65,6 +65,10 @@
         setUserSwitcherController(qsPanel.getHost().getUserSwitcherController());
     }
 
+    public boolean hasMultipleUsers() {
+        return mUserListener.getCount() != 0;
+    }
+
     public void setUserSwitcherController(UserSwitcherController userSwitcherController) {
         mUserSwitcherController = userSwitcherController;
         registerListener();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
deleted file mode 100644
index d0c14f1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ /dev/null
@@ -1,1119 +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.statusbar.phone;
-
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-import android.animation.LayoutTransition;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo;
-import android.app.ActivityManagerNative;
-import android.app.ActivityOptions;
-import android.app.IActivityManager;
-import android.app.ITaskStackListener;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.DragEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.PopupMenu;
-import android.widget.Toast;
-
-import com.android.internal.content.PackageMonitor;
-import com.android.systemui.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Container for application icons that appear in the navigation bar. Their appearance is similar
- * to the launcher hotseat. Clicking an icon launches or activates the associated activity. A long
- * click will trigger a drag to allow the icons to be reordered. As an icon is dragged the other
- * icons shift to make space for it to be dropped. These layout changes are animated.
- * Navigation bar contains both pinned and unpinned apps: pinned in the left part, unpinned in the
- * right part, with no separator in between.
- */
-class NavigationBarApps extends LinearLayout
-        implements NavigationBarAppsModel.OnAppsChangedListener {
-    public final static boolean DEBUG = false;
-    private final static String TAG = "NavigationBarApps";
-
-    /**
-     * Intent extra to store user serial number.
-     */
-    static final String EXTRA_PROFILE = "profile";
-
-    // There are separate NavigationBarApps view instances for landscape vs. portrait, but they
-    // share the data model.
-    private static NavigationBarAppsModel sAppsModel;
-
-    private final PackageManager mPackageManager;
-    private final UserManager mUserManager;
-    private final LayoutInflater mLayoutInflater;
-    private final AppPackageMonitor mAppPackageMonitor;
-    private final WindowManager mWindowManager;
-
-
-    // This view has two roles:
-    // 1) If the drag started outside the pinned apps list, it is a placeholder icon with a null
-    // tag.
-    // 2) If the drag started inside the pinned apps list, it is the icon for the app being dragged
-    // with the associated AppInfo tag.
-    // The icon is set invisible for the duration of the drag, creating a visual space for a drop.
-    // When the user is not dragging this member is null.
-    private ImageView mDragView;
-
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                int currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-                onUserSwitched(currentUserId);
-            } else if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
-                UserHandle removedProfile = intent.getParcelableExtra(Intent.EXTRA_USER);
-                onManagedProfileRemoved(removedProfile);
-            }
-        }
-    };
-
-    // Layout params for the window that contains the anchor for the popup menus.
-    // We need to create a window for a popup menu because the NavBar window is too narrow and can't
-    // contain the menu.
-    private final WindowManager.LayoutParams mPopupAnchorLayoutParams;
-    // View that contains the anchor for popup menus. The view occupies the whole screen, and
-    // has a child that will be moved to make the menu to appear where we need it.
-    private final ViewGroup mPopupAnchor;
-    private final PopupMenu mPopupMenu;
-
-    /**
-     * True if popup menu code is busy with a popup operation.
-     * Attempting  to show a popup menu or to add menu items while it's returning true will
-     * corrupt/crash the app.
-     */
-    private boolean mIsPopupInUse = false;
-    private final int [] mClickedIconLocation = new int[2];
-
-    public NavigationBarApps(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        if (sAppsModel == null) {
-            sAppsModel = new NavigationBarAppsModel(context);
-        }
-        mPackageManager = context.getPackageManager();
-        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        mLayoutInflater = LayoutInflater.from(context);
-        mAppPackageMonitor = new AppPackageMonitor();
-
-        // Dragging an icon removes and adds back the dragged icon. Use the layout transitions to
-        // trigger animation. By default all transitions animate, so turn off the unneeded ones.
-        LayoutTransition transition = new LayoutTransition();
-        // Don't trigger on disappear. Adding the view will trigger the layout animation.
-        transition.disableTransitionType(LayoutTransition.DISAPPEARING);
-        // Don't animate the dragged icon itself.
-        transition.disableTransitionType(LayoutTransition.APPEARING);
-        // When an icon is dragged off the shelf, start sliding the other icons over immediately
-        // to match the parent view's animation.
-        transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
-        transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 0);
-        setLayoutTransition(transition);
-
-        TaskStackListener taskStackListener = new TaskStackListener();
-        IActivityManager iam = ActivityManagerNative.getDefault();
-        try {
-            iam.registerTaskStackListener(taskStackListener);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "registerTaskStackListener failed", e);
-        }
-
-        mPopupAnchorLayoutParams =
-                new WindowManager.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT,
-                        ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
-                        WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
-                        WindowManager.LayoutParams.FLAG_FULLSCREEN
-                                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
-                                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
-                        PixelFormat.TRANSLUCENT);
-        mPopupAnchorLayoutParams.setTitle("ShelfMenuAnchor");
-
-        mPopupAnchor = (ViewGroup) mLayoutInflater.inflate(R.layout.shelf_menu_anchor, null);
-
-        ImageView anchorButton =
-                (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor);
-        mPopupMenu = new PopupMenu(context, anchorButton);
-    }
-
-    // Monitor that catches events like "app uninstalled".
-    private class AppPackageMonitor extends PackageMonitor {
-        @Override
-        public void onPackageRemoved(String packageName, int uid) {
-            postUnpinIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
-            super.onPackageRemoved(packageName, uid);
-        }
-
-        @Override
-        public void onPackageModified(String packageName) {
-            postUnpinIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
-            super.onPackageModified(packageName);
-        }
-
-        @Override
-        public void onPackagesAvailable(String[] packages) {
-            if (isReplacing()) {
-                UserHandle user = new UserHandle(getChangingUserId());
-
-                for (String packageName : packages) {
-                    postUnpinIfUnlauncheable(packageName, user);
-                }
-            }
-            super.onPackagesAvailable(packages);
-        }
-
-        @Override
-        public void onPackagesUnavailable(String[] packages) {
-            if (!isReplacing()) {
-                UserHandle user = new UserHandle(getChangingUserId());
-
-                for (String packageName : packages) {
-                    postUnpinIfUnlauncheable(packageName, user);
-                }
-            }
-            super.onPackagesUnavailable(packages);
-        }
-    }
-
-    private void postUnpinIfUnlauncheable(final String packageName, final UserHandle user) {
-        // This method doesn't necessarily get called in the main thread. Redirect the call into
-        // the main thread.
-        post(new Runnable() {
-            @Override
-            public void run() {
-                if (!isAttachedToWindow()) return;
-                unpinIfUnlauncheable(packageName, user);
-            }
-        });
-    }
-
-    private void unpinIfUnlauncheable(String packageName, UserHandle user) {
-        // Unpin icons for all apps that match a package that perhaps became unlauncheable.
-        boolean appsWereUnpinned = false;
-        for(int i = getChildCount() - 1; i >= 0; --i) {
-            View child = getChildAt(i);
-            AppButtonData appButtonData = (AppButtonData)child.getTag();
-            if (appButtonData == null) continue;  // Skip the drag placeholder.
-
-            if (!appButtonData.pinned) continue;
-
-            AppInfo appInfo = appButtonData.appInfo;
-            if (!appInfo.getUser().equals(user)) continue;
-
-            ComponentName appComponentName = appInfo.getComponentName();
-            if (!appComponentName.getPackageName().equals(packageName)) continue;
-
-            if (sAppsModel.resolveApp(appInfo) != null) {
-                continue;
-            }
-
-            appButtonData.pinned = false;
-            appsWereUnpinned = true;
-
-            if (appButtonData.isEmpty()) {
-                removeViewAt(i);
-            }
-        }
-        if (appsWereUnpinned) {
-            savePinnedApps();
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-      super.onAttachedToWindow();
-        // When an icon is dragged out of the pinned area this view's width changes, which causes
-        // the parent container's layout to change and the divider and recents icons to shift left.
-        // Animate the parent's CHANGING transition.
-        ViewGroup parent = (ViewGroup) getParent();
-        LayoutTransition transition = new LayoutTransition();
-        transition.disableTransitionType(LayoutTransition.APPEARING);
-        transition.disableTransitionType(LayoutTransition.DISAPPEARING);
-        transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
-        transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
-        transition.enableTransitionType(LayoutTransition.CHANGING);
-        parent.setLayoutTransition(transition);
-
-        sAppsModel.setCurrentUser(ActivityManager.getCurrentUser());
-        recreatePinnedAppButtons();
-        updateRecentApps();
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-        mContext.registerReceiver(mBroadcastReceiver, filter);
-
-        mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
-        sAppsModel.addOnAppsChangedListener(this);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mContext.unregisterReceiver(mBroadcastReceiver);
-        mAppPackageMonitor.unregister();
-        sAppsModel.removeOnAppsChangedListener(this);
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        if (mIsPopupInUse && !isShown()) {
-            // Hide the popup if current view became invisible.
-            shutdownPopupMenu();
-        }
-    }
-
-    private void addAppButton(AppButtonData appButtonData) {
-        ImageView button = createAppButton();
-        updateApp(button, appButtonData);
-        addView(button);
-    }
-
-    private List<AppInfo> getPinnedApps() {
-        List<AppInfo> apps = new ArrayList<AppInfo>();
-        int childCount = getChildCount();
-        for (int i = 0; i != childCount; ++i) {
-            View child = getChildAt(i);
-            AppButtonData appButtonData = (AppButtonData)child.getTag();
-            if (appButtonData == null) continue;  // Skip the drag placeholder.
-            if(!appButtonData.pinned) continue;
-            apps.add(appButtonData.appInfo);
-        }
-        return apps;
-    }
-
-    /**
-     * Creates an ImageView icon for each pinned app. Removes any existing icons. May be called
-     * to synchronize the current view with the shared data mode.
-     */
-    private void recreatePinnedAppButtons() {
-        // Remove any existing icon buttons.
-        removeAllViews();
-
-        List<AppInfo> apps = sAppsModel.getApps();
-        int appCount = apps.size();
-        for (int i = 0; i < appCount; i++) {
-            AppInfo app = apps.get(i);
-            addAppButton(new AppButtonData(app, true /* pinned */));
-        }
-    }
-
-    /**
-     * Saves pinned apps stored in app icons into the data model.
-     */
-    private void savePinnedApps() {
-        sAppsModel.setApps(getPinnedApps());
-    }
-
-    /**
-     * Creates a new ImageView for an app, inflated from R.layout.navigation_bar_app_item.
-     */
-    private ImageView createAppButton() {
-        ImageView button = (ImageView) mLayoutInflater.inflate(
-                R.layout.navigation_bar_app_item, this, false /* attachToRoot */);
-        button.setOnHoverListener(new AppHoverListener());
-        button.setOnClickListener(new AppClickListener());
-        button.setOnContextClickListener(new AppContextClickListener());
-        // TODO: Ripple effect. Use either KeyButtonRipple or the default ripple background.
-        button.setOnLongClickListener(new AppLongClickListener());
-        button.setOnDragListener(new AppIconDragListener());
-        return button;
-    }
-
-    private class AppLongClickListener implements View.OnLongClickListener {
-        @Override
-        public boolean onLongClick(View v) {
-            mDragView = (ImageView) v;
-            AppButtonData appButtonData = (AppButtonData) v.getTag();
-            startAppDrag(mDragView, appButtonData.appInfo);
-            return true;
-        }
-    }
-
-    /**
-     * Returns the human-readable name for an activity's package or null.
-     * TODO: Cache the labels, perhaps in an LruCache.
-     */
-    @Nullable
-    private CharSequence getAppLabel(AppInfo appInfo) {
-        NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo);
-        if (resolvedApp == null) return null;
-
-        CharSequence unbadgedLabel = resolvedApp.ri.loadLabel(mPackageManager);
-        return mUserManager.getBadgedLabelForUser(unbadgedLabel, appInfo.getUser());
-    }
-
-    /** Helper function to start dragging an app icon (either pinned or recent). */
-    static void startAppDrag(ImageView icon, AppInfo appInfo) {
-        // The drag data is an Intent to launch the activity.
-        Intent mainIntent = Intent.makeMainActivity(appInfo.getComponentName());
-        UserManager userManager =
-                (UserManager) icon.getContext().getSystemService(Context.USER_SERVICE);
-        long userSerialNumber = userManager.getSerialNumberForUser(appInfo.getUser());
-        mainIntent.putExtra(EXTRA_PROFILE, userSerialNumber);
-        ClipData dragData = ClipData.newIntent("", mainIntent);
-        // Use the ImageView to create the shadow.
-        View.DragShadowBuilder shadow = new AppIconDragShadowBuilder(icon);
-        // Use a global drag because the icon might be dragged into the launcher.
-        icon.startDrag(dragData, shadow, null /* myLocalState */, View.DRAG_FLAG_GLOBAL);
-    }
-
-    @Override
-    public boolean dispatchDragEvent(DragEvent event) {
-        // ACTION_DRAG_ENTERED is handled by each individual app icon drag listener.
-        boolean childHandled = super.dispatchDragEvent(event);
-
-        // Other drag types are handled once per drag by this view. This is handled explicitly
-        // because attaching a DragListener to this ViewGroup does not work -- the DragListener in
-        // the children consumes the drag events.
-        boolean handled = false;
-        switch (event.getAction()) {
-            case DragEvent.ACTION_DRAG_STARTED:
-                handled = onDragStarted(event);
-                break;
-            case DragEvent.ACTION_DRAG_ENDED:
-                handled = onDragEnded();
-                break;
-            case DragEvent.ACTION_DROP:
-                handled = onDrop(event);
-                break;
-            case DragEvent.ACTION_DRAG_EXITED:
-                handled = onDragExited();
-                break;
-        }
-
-        return handled || childHandled;
-    }
-
-    /** Returns true if a drag should be handled. */
-    private static boolean canAcceptDrag(DragEvent event) {
-        // Poorly behaved apps might not provide a clip description.
-        if (event.getClipDescription() == null) {
-            return false;
-        }
-        // The event must contain an intent.
-        return event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT);
-    }
-
-    /**
-     * Sets up for a drag. Runs once per drag operation. Returns true if the data represents
-     * an app shortcut and will be accepted for a drop.
-     */
-    private boolean onDragStarted(DragEvent event) {
-        if (DEBUG) Slog.d(TAG, "onDragStarted");
-
-        // Ensure that an app shortcut is being dragged.
-        if (!canAcceptDrag(event)) {
-            return false;
-        }
-
-        // If there are no pinned apps this view will be collapsed, but the user still needs some
-        // empty space to use as a drag target.
-        if (getChildCount() == 0) {
-            mDragView = createPlaceholderDragView(0);
-        }
-
-        // If this is an existing icon being reordered, hide the app icon. The drag shadow will
-        // continue to draw.
-        if (mDragView != null) {
-            mDragView.setVisibility(View.INVISIBLE);
-        }
-
-        // Listen for the drag end event.
-        return true;
-    }
-
-    /**
-     * Creates a blank icon-sized View to create an empty space during a drag.
-     */
-    private ImageView createPlaceholderDragView(int index) {
-        ImageView button = createAppButton();
-        addView(button, index);
-        return button;
-    }
-
-    /**
-     * Returns initial index for a new app that doesn't exist in Shelf.
-     * Such apps get created by dragging them into Shelf from other apps or by dragging from Shelf
-     * and then back, or by removing from shelf as an intermediate step of pinning an app via menu.
-     * @param indexHint Initial proposed position for the item.
-     * @param isAppPinned True if the app being dragged is pinned.
-     */
-    int getNewAppIndex(int indexHint, boolean isAppPinned) {
-        int i;
-        if (isAppPinned) {
-            // For a pinned app, find the rightmost position to the left of the target that has a
-            // pinned app. We'll insert to the right of that position.
-            for (i = indexHint; i > 0; --i) {
-                View v = getChildAt(i - 1);
-                AppButtonData targetButtonData = (AppButtonData) v.getTag();
-                if (targetButtonData.pinned) break;
-            }
-        } else {
-            // For an unpinned app, find the leftmost position to the right of the target that has
-            // an unpinned app. We'll insert to the left of that position.
-            int childCount = getChildCount();
-            for (i = indexHint; i < childCount; ++i) {
-                View v = getChildAt(i);
-                AppButtonData targetButtonData = (AppButtonData) v.getTag();
-                if (!targetButtonData.pinned) break;
-            }
-        }
-        return i;
-    }
-
-    /**
-     * Handles a drag entering an existing icon. Not implemented in the drag listener because it
-     * needs to use LinearLayout/ViewGroup methods.
-     */
-    private void onDragEnteredIcon(View target) {
-        if (DEBUG) Slog.d(TAG, "onDragEntered " + indexOfChild(target));
-
-        int targetIndex = indexOfChild(target);
-
-        // If the drag didn't start from an existing shelf icon, add an invisible placeholder to
-        // create empty space for the user to drag into.
-        if (mDragView == null) {
-            mDragView = createPlaceholderDragView(getNewAppIndex(targetIndex, true));
-            return;
-        }
-
-        // If the user is dragging on top of the original icon location, do nothing.
-        if (target == mDragView) {
-            return;
-        }
-
-        // "Move" the dragged app by removing it and adding it back at the target location.
-        AppButtonData targetButtonData = (AppButtonData) target.getTag();
-        int dragViewIndex = indexOfChild(mDragView);
-        AppButtonData dragViewButtonData = (AppButtonData) mDragView.getTag();
-        // Calculating whether the dragged app is pinned. If the app came from outside if the shelf,
-        // in which case dragViewButtonData == null, it's a new app that we'll pin. Otherwise, the
-        // button data is defined, and we look whether that existing app is pinned.
-        boolean isAppPinned = dragViewButtonData == null || dragViewButtonData.pinned;
-
-        if (dragViewIndex == -1) {
-            // Drag view exists, but is not a child, which means that the drag has started at or
-            // already visited shelf, then left it, and now is entering it again.
-            targetIndex = getNewAppIndex(targetIndex, isAppPinned);
-        } else if (dragViewIndex < targetIndex) {
-            // The dragged app is currently at the left of the view where the drag is.
-            // We shouldn't allow moving a pinned app to the right of the unpinned app.
-            if (!targetButtonData.pinned && isAppPinned) return;
-        } else {
-            // The dragged app is currently at the right of the view where the drag is.
-            // We shouldn't allow moving a unpinned app to the left of the pinned app.
-            if (targetButtonData.pinned && !isAppPinned) return;
-        }
-
-        // This works, but is subtle:
-        // * If dragViewIndex > targetIndex then the dragged app is moving from right to left and
-        //   the dragged app will be added in front of the target.
-        // * If dragViewIndex < targetIndex then the dragged app is moving from left to right.
-        //   Removing the drag view will shift the later views one position to the left. Adding
-        //   the view at targetIndex will therefore place the app *after* the target.
-        removeView(mDragView);
-        addView(mDragView, targetIndex);
-    }
-
-    private boolean onDrop(DragEvent event) {
-        if (DEBUG) Slog.d(TAG, "onDrop");
-
-        // An earlier drag event might have canceled the drag. If so, there is nothing to do.
-        if (mDragView == null) {
-            return true;
-        }
-
-        boolean dragResult = true;
-        AppInfo appInfo = getAppFromDragEvent(event);
-        if (appInfo == null) {
-            // This wasn't a valid drop. Clean up the placeholder.
-            removePlaceholderDragViewIfNeeded();
-            dragResult = false;
-        } else if (mDragView.getTag() == null) {
-            // This is a drag that adds a new app. Convert the placeholder to a real icon.
-            updateApp(mDragView, new AppButtonData(appInfo, true /* pinned */));
-        }
-        endDrag();
-        return dragResult;
-    }
-
-    /** Cleans up at the end of a drag. */
-    private void endDrag() {
-        // An earlier drag event might have canceled the drag. If so, there is nothing to do.
-        if (mDragView == null) return;
-
-        mDragView.setVisibility(View.VISIBLE);
-        mDragView = null;
-        savePinnedApps();
-        // Add recent tasks to the info of the potentially added app.
-        updateRecentApps();
-    }
-
-    /** Returns an app info from a DragEvent, or null if the data wasn't valid. */
-    private AppInfo getAppFromDragEvent(DragEvent event) {
-        ClipData data = event.getClipData();
-        if (data == null) {
-            return null;
-        }
-        if (data.getItemCount() != 1) {
-            return null;
-        }
-        ClipData.Item item = data.getItemAt(0);
-        if (item == null) {
-            return null;
-        }
-        Intent intent = item.getIntent();
-        if (intent == null) {
-            return null;
-        }
-        long userSerialNumber = intent.getLongExtra(EXTRA_PROFILE, -1);
-        if (userSerialNumber == -1) {
-            return null;
-        }
-        UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
-        if (appUser == null) {
-            return null;
-        }
-        ComponentName componentName = intent.getComponent();
-        if (componentName == null) {
-            return null;
-        }
-        AppInfo appInfo = new AppInfo(componentName, appUser);
-        if (sAppsModel.resolveApp(appInfo) == null) {
-            return null;
-        }
-        return appInfo;
-    }
-
-    /** Updates the app at a given view index. */
-    private void updateApp(ImageView button, AppButtonData appButtonData) {
-        CharSequence appLabel = getAppLabel(appButtonData.appInfo);
-        button.setContentDescription(appLabel);
-
-        button.setTag(appButtonData);
-        new GetActivityIconTask(mPackageManager, button).execute(appButtonData);
-    }
-
-    /** Removes the empty placeholder view. */
-    private void removePlaceholderDragViewIfNeeded() {
-        // If the drag has ended already there is nothing to do.
-        if (mDragView == null) {
-            return;
-        }
-        removeView(mDragView);
-    }
-
-    /** Cleans up at the end of the drag. */
-    private boolean onDragEnded() {
-        if (DEBUG) Slog.d(TAG, "onDragEnded");
-        // If the icon wasn't already dropped into the app list then remove the placeholder.
-        removePlaceholderDragViewIfNeeded();
-        endDrag();
-        return true;
-    }
-
-    /** Handles the dragged icon exiting the bounds of this view during the drag. */
-    private boolean onDragExited() {
-        if (DEBUG) Slog.d(TAG, "onDragExited");
-        // Remove the placeholder. It will be added again if the user drags the icon back over
-        // the shelf.
-        removePlaceholderDragViewIfNeeded();
-        return true;
-    }
-
-    /** Drag listener for individual app icons. */
-    private class AppIconDragListener implements View.OnDragListener {
-        @Override
-        public boolean onDrag(View v, DragEvent event) {
-            switch (event.getAction()) {
-                case DragEvent.ACTION_DRAG_STARTED: {
-                    // Every button listens for drag events in order to detect enter/exit.
-                    return canAcceptDrag(event);
-                }
-                case DragEvent.ACTION_DRAG_ENTERED: {
-                    // Forward to NavigationBarApps.
-                    onDragEnteredIcon(v);
-                    return false;
-                }
-            }
-            return false;
-        }
-    }
-
-    /**
-     * Brings the menu popup to closed state.
-     * Can be called at any stage of the asynchronous process of showing a menu.
-     */
-    private void shutdownPopupMenu() {
-        mWindowManager.removeView(mPopupAnchor);
-        mPopupMenu.dismiss();
-    }
-
-    /**
-     * Shows already prepopulated popup menu using appIcon for anchor location.
-     */
-    private void showPopupMenu(ImageView appIcon) {
-        // Movable view inside the popup anchor view. It serves as the actual anchor for the
-        // menu.
-        final ImageView anchorButton =
-                (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor);
-        // Set same drawable as for the clicked button to have same size.
-        anchorButton.setImageDrawable(appIcon.getDrawable());
-
-        // Move the anchor button to the position of the app button.
-        appIcon.getLocationOnScreen(mClickedIconLocation);
-        anchorButton.setTranslationX(mClickedIconLocation[0]);
-        anchorButton.setTranslationY(mClickedIconLocation[1]);
-
-        final OnAttachStateChangeListener onAttachStateChangeListener =
-                new OnAttachStateChangeListener() {
-                    @Override
-                    public void onViewAttachedToWindow(View v) {
-                        mPopupMenu.show();
-                    }
-
-                    @Override
-                    public void onViewDetachedFromWindow(View v) {}
-                };
-        anchorButton.addOnAttachStateChangeListener(onAttachStateChangeListener);
-
-        mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
-            @Override
-            public void onDismiss(PopupMenu menu) {
-                // FYU: thorough testing for closing menu either by the user or via
-                // shutdownPopupMenu() called at various moments of the menu creation, revealed that
-                // 'onDismiss' is guaranteed to be called after each invocation of showPopupMenu.
-                mWindowManager.removeView(mPopupAnchor);
-                anchorButton.removeOnAttachStateChangeListener(onAttachStateChangeListener);
-                mPopupMenu.setOnDismissListener(null);
-                mPopupMenu.getMenu().clear();
-                mIsPopupInUse = false;
-            }
-        });
-
-        mWindowManager.addView(mPopupAnchor, mPopupAnchorLayoutParams);
-        mIsPopupInUse = true;
-    }
-
-    private void activateTask(int taskPersistentId) {
-        // Launch or bring the activity to front.
-        final IActivityManager iAm = ActivityManagerNative.getDefault();
-        try {
-            iAm.startActivityFromRecents(taskPersistentId, null /* options */);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Exception when activating a recent task", e);
-        } catch (IllegalArgumentException e) {
-            Slog.e(TAG, "Exception when activating a recent task", e);
-        }
-    }
-
-    /**
-     * Adds to the popup menu items for activating each of tasks in the specified list.
-     */
-    private void populateLaunchMenu(AppButtonData appButtonData) {
-        Menu menu = mPopupMenu.getMenu();
-        int taskCount = appButtonData.getTaskCount();
-        for (int i = 0; i < taskCount; ++i) {
-            final RecentTaskInfo taskInfo = appButtonData.tasks.get(i);
-            MenuItem item = menu.add(getActivityForTask(taskInfo).flattenToShortString());
-            item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                @Override
-                public boolean onMenuItemClick(MenuItem item) {
-                    activateTask(taskInfo.persistentId);
-                    return true;
-                }
-            });
-        }
-    }
-
-    /**
-     * Shows a task selection menu for clicked or hovered-over apps that have more than 1 running
-     * tasks.
-     */
-    void maybeShowLaunchMenu(ImageView appIcon) {
-        if (mIsPopupInUse) return;
-        AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
-        if (appButtonData.getTaskCount() <= 1) return;
-
-        populateLaunchMenu(appButtonData);
-        showPopupMenu(appIcon);
-    }
-
-    /**
-     * A listener for hovering over an app icon.
-     */
-    private class AppHoverListener implements View.OnHoverListener {
-        private final long DELAY_MILLIS = 1000;
-        private Runnable mShowMenuCallback;
-
-        @Override
-        public boolean onHover(final View v, MotionEvent event) {
-            if (mShowMenuCallback == null) {
-                mShowMenuCallback = new Runnable() {
-                    @Override
-                    public void run() {
-                        maybeShowLaunchMenu((ImageView) v);
-                    }
-                };
-            }
-
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_HOVER_ENTER:
-                    postDelayed(mShowMenuCallback, DELAY_MILLIS);
-                    break;
-                case MotionEvent.ACTION_HOVER_EXIT:
-                    removeCallbacks(mShowMenuCallback);
-                    break;
-            }
-            return false;
-        }
-    }
-
-    /**
-     * A click listener that launches an activity.
-     */
-    private class AppClickListener implements View.OnClickListener {
-        private void launchApp(AppInfo appInfo, View anchor) {
-            NavigationBarAppsModel.ResolvedApp resolvedApp = sAppsModel.resolveApp(appInfo);
-            if (resolvedApp == null) {
-                Toast.makeText(
-                        getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-                return;
-            }
-
-            Intent launchIntent = resolvedApp.launchIntent;
-
-            // Play a scale-up animation while launching the activity.
-            // TODO: Consider playing a different animation, or no animation, if the activity is
-            // already open in a visible window. In that case we should move the task to front
-            // with minimal animation, perhaps using ActivityManager.moveTaskToFront().
-            Rect sourceBounds = new Rect();
-            anchor.getBoundsOnScreen(sourceBounds);
-            ActivityOptions opts =
-                    ActivityOptions.makeScaleUpAnimation(
-                            anchor, 0, 0, anchor.getWidth(), anchor.getHeight());
-            Bundle optsBundle = opts.toBundle();
-            launchIntent.setSourceBounds(sourceBounds);
-
-            mContext.startActivityAsUser(launchIntent, optsBundle, appInfo.getUser());
-        }
-
-        @Override
-        public void onClick(View v) {
-            AppButtonData appButtonData = (AppButtonData) v.getTag();
-
-            if (appButtonData.getTaskCount() == 0) {
-                launchApp(appButtonData.appInfo, v);
-            } else {
-                // Activate latest task.
-                activateTask(appButtonData.tasks.get(0).persistentId);
-
-                maybeShowLaunchMenu((ImageView) v);
-            }
-        }
-    }
-
-    /**
-     * Context click listener that shows app's context menu.
-     */
-    private class AppContextClickListener implements View.OnContextClickListener {
-        void updateState(ImageView appIcon) {
-            savePinnedApps();
-            if (DEBUG) {
-                AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
-                new GetActivityIconTask(mPackageManager, appIcon).execute(appButtonData);
-            }
-        }
-
-        /**
-         * Adds to the popup menu items for pinning and unpinning the app in the shelf.
-         */
-        void populateContextMenu(final ImageView appIcon) {
-            final AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
-            Menu menu = mPopupMenu.getMenu();
-            if (appButtonData.pinned) {
-                menu.add("Unpin").
-                        setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                            @Override
-                            public boolean onMenuItemClick(MenuItem item) {
-                                appButtonData.pinned = false;
-                                removeView(appIcon);
-                                if (!appButtonData.isEmpty()) {
-                                    // If the app has running tasks, re-add it to the end of shelf
-                                    // after unpinning.
-                                    addView(appIcon);
-                                }
-                                updateState(appIcon);
-                                return true;
-                            }
-                        });
-            } else {
-                menu.add("Pin").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        appButtonData.pinned = true;
-                        removeView(appIcon);
-                        // Re-add the pinned icon to the end of the pinned list.
-                        addView(appIcon, getNewAppIndex(getChildCount(), true));
-                        updateState(appIcon);
-                        return true;
-                    }
-                });
-            }
-        }
-
-        @Override
-        public boolean onContextClick(View v) {
-            if (mIsPopupInUse) return true;
-            ImageView appIcon = (ImageView) v;
-            populateContextMenu(appIcon);
-            showPopupMenu(appIcon);
-            return true;
-        }
-    }
-
-    private void onUserSwitched(int currentUserId) {
-        sAppsModel.setCurrentUser(currentUserId);
-        recreatePinnedAppButtons();
-    }
-
-    private void onManagedProfileRemoved(UserHandle removedProfile) {
-        // Unpin apps from the removed profile.
-        boolean itemsWereUnpinned = false;
-        for(int i = getChildCount() - 1; i >= 0; --i) {
-            View view = getChildAt(i);
-            AppButtonData appButtonData = (AppButtonData)view.getTag();
-            if (appButtonData == null) return;  // Skip the drag placeholder.
-            if (!appButtonData.pinned) continue;
-            if (!appButtonData.appInfo.getUser().equals(removedProfile)) continue;
-
-            appButtonData.pinned = false;
-            itemsWereUnpinned = true;
-            if (appButtonData.isEmpty()) {
-                removeViewAt(i);
-            }
-        }
-        if (itemsWereUnpinned) {
-            savePinnedApps();
-        }
-    }
-
-    /**
-     * Returns app data for a button that matches the provided app info, if it exists, or null
-     * otherwise.
-     */
-    private AppButtonData findAppButtonData(AppInfo appInfo) {
-        int size = getChildCount();
-        for (int i = 0; i < size; ++i) {
-            View view = getChildAt(i);
-            AppButtonData appButtonData = (AppButtonData)view.getTag();
-            if (appButtonData == null) continue;  // Skip the drag placeholder.
-            if (appButtonData.appInfo.equals(appInfo)) {
-                return appButtonData;
-            }
-        }
-        return null;
-    }
-
-    private void updateTasks(List<RecentTaskInfo> tasks) {
-        // Remove tasks from all app buttons.
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            View view = getChildAt(i);
-            AppButtonData appButtonData = (AppButtonData)view.getTag();
-            if (appButtonData == null) return;  // Skip the drag placeholder.
-            appButtonData.clearTasks();
-        }
-
-        // Re-add tasks to app buttons, adding new buttons if needed.
-        int size = tasks.size();
-        for (int i = 0; i != size; ++i) {
-            RecentTaskInfo task = tasks.get(i);
-            AppInfo taskAppInfo = taskToAppInfo(task);
-            if (taskAppInfo == null) continue;
-            AppButtonData appButtonData = findAppButtonData(taskAppInfo);
-            if (appButtonData == null) {
-                appButtonData = new AppButtonData(taskAppInfo, false);
-                addAppButton(appButtonData);
-            }
-            appButtonData.addTask(task);
-        }
-
-        // Remove unpinned apps that now have no tasks.
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            View view = getChildAt(i);
-            AppButtonData appButtonData = (AppButtonData)view.getTag();
-            if (appButtonData == null) return;  // Skip the drag placeholder.
-            if (appButtonData.isEmpty()) {
-                removeViewAt(i);
-            }
-        }
-
-        if (DEBUG) {
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                View view = getChildAt(i);
-                AppButtonData appButtonData = (AppButtonData)view.getTag();
-                if (appButtonData == null) return;  // Skip the drag placeholder.
-                new GetActivityIconTask(mPackageManager, (ImageView )view).execute(appButtonData);
-
-            }
-        }
-    }
-
-    private void updateRecentApps() {
-        ActivityManager activityManager =
-                (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-        // TODO: Should this be getRunningTasks?
-        List<RecentTaskInfo> recentTasks = activityManager.getRecentTasksForUser(
-                ActivityManager.getMaxAppRecentsLimitStatic(),
-                ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
-                        ActivityManager.RECENT_IGNORE_UNAVAILABLE |
-                        ActivityManager.RECENT_INCLUDE_PROFILES,
-                UserHandle.USER_CURRENT);
-        if (DEBUG) Slog.d(TAG, "Got recents " + recentTasks.size());
-        updateTasks(recentTasks);
-    }
-
-    private static ComponentName getActivityForTask(RecentTaskInfo task) {
-        // If the task was started from an alias, return the actual activity component that was
-        // initially started.
-        if (task.origActivity != null) {
-            return task.origActivity;
-        }
-        // Prefer the first activity of the task.
-        if (task.baseActivity != null) {
-            return task.baseActivity;
-        }
-        // Then goes the activity that started the task.
-        if (task.realActivity != null) {
-            return task.realActivity;
-        }
-        // This should not happen, but fall back to the base intent's activity component name.
-        return task.baseIntent.getComponent();
-    }
-
-    private ComponentName getLaunchComponentForPackage(String packageName, int userId) {
-        // This code is based on ApplicationPackageManager.getLaunchIntentForPackage.
-        PackageManager packageManager = mContext.getPackageManager();
-
-        // First see if the package has an INFO activity; the existence of
-        // such an activity is implied to be the desired front-door for the
-        // overall package (such as if it has multiple launcher entries).
-        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
-        intentToResolve.addCategory(Intent.CATEGORY_INFO);
-        intentToResolve.setPackage(packageName);
-        List<ResolveInfo> ris = packageManager.queryIntentActivitiesAsUser(
-                intentToResolve, 0, userId);
-
-        // Otherwise, try to find a main launcher activity.
-        if (ris == null || ris.size() <= 0) {
-            // reuse the intent instance
-            intentToResolve.removeCategory(Intent.CATEGORY_INFO);
-            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
-            intentToResolve.setPackage(packageName);
-            ris = packageManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);
-        }
-        if (ris == null || ris.size() <= 0) {
-            Slog.i(TAG, "Failed to build intent for " + packageName);
-            return null;
-        }
-        return new ComponentName(ris.get(0).activityInfo.packageName,
-                ris.get(0).activityInfo.name);
-    }
-
-    private AppInfo taskToAppInfo(RecentTaskInfo task) {
-        ComponentName componentName = getActivityForTask(task);
-        UserHandle taskUser = new UserHandle(task.userId);
-        AppInfo appInfo = new AppInfo(componentName, taskUser);
-
-        if (sAppsModel.resolveApp(appInfo) == null) {
-            // If task's activity is not launcheable, fall back to a launch component of the
-            // task's package.
-            ComponentName component = getLaunchComponentForPackage(
-                    componentName.getPackageName(), task.userId);
-
-            if (component == null) {
-                return null;
-            }
-
-            appInfo = new AppInfo(component, taskUser);
-        }
-
-        return appInfo;
-    }
-
-    /**
-     * A listener that updates the app buttons whenever the recents task stack changes.
-     */
-    private class TaskStackListener extends ITaskStackListener.Stub {
-        @Override
-        public void onTaskStackChanged() throws RemoteException {
-            // Post the message back to the UI thread.
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    if (isAttachedToWindow()) {
-                        updateRecentApps();
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void onActivityPinned() {
-        }
-    }
-
-    @Override
-    public void onPinnedAppsChanged() {
-        if (getPinnedApps().equals(sAppsModel.getApps())) return;
-        recreatePinnedAppButtons();
-        updateRecentApps();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
deleted file mode 100644
index 76a9798..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
+++ /dev/null
@@ -1,364 +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.statusbar.phone;
-
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Data model and controller for app icons appearing in the navigation bar. The data is stored on
- * disk in SharedPreferences. Each icon has a separate pref entry consisting of a flattened
- * ComponentName.
- */
-class NavigationBarAppsModel {
-    public interface OnAppsChangedListener {
-        void onPinnedAppsChanged();
-    }
-
-    public class ResolvedApp {
-        Intent launchIntent;
-        ResolveInfo ri;
-    }
-
-    private final static String TAG = "NavigationBarAppsModel";
-
-    // Default number of apps to load initially.
-    private final static int NUM_INITIAL_APPS = 4;
-
-    // Preferences file name.
-    private final static String SHARED_PREFERENCES_NAME = "com.android.systemui.navbarapps";
-
-    // Preference name for the version of the other preferences.
-    private final static String VERSION_PREF = "version";
-
-    // Current version number for preferences.
-    private final static int CURRENT_VERSION = 3;
-
-    // Preference name for the number of app icons.
-    private final static String APP_COUNT_PREF = "app_count";
-
-    // Preference name prefix for each app's info. The actual pref has an integer appended to it.
-    private final static String APP_PREF_PREFIX = "app_";
-
-    // User serial number prefix for each app's info. The actual pref has an integer appended to it.
-    private final static String APP_USER_PREFIX = "app_user_";
-
-    // Character separating current user serial number from the user-specific part of a pref.
-    // Example "22|app_user_2" - when logged as user with serial 22, we'll use this pref for the
-    // user serial of the third app of the logged-in user.
-    private final static char USER_SEPARATOR = '|';
-
-    private final Context mContext;
-    private final UserManager mUserManager;
-    private final SharedPreferences mPrefs;
-
-    // Apps are represented as an ordered list of app infos.
-    private List<AppInfo> mApps = new ArrayList<AppInfo>();
-
-    private List<OnAppsChangedListener> mOnAppsChangedListeners =
-            new ArrayList<OnAppsChangedListener>();
-
-    // Id of the current user.
-    private int mCurrentUserId = -1;
-
-    // Serial number of the current user.
-    private long mCurrentUserSerialNumber = -1;
-
-    public NavigationBarAppsModel(Context context) {
-        mContext = context;
-        mPrefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
-        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-
-        int version = mPrefs.getInt(VERSION_PREF, -1);
-        if (version != CURRENT_VERSION) {
-            // Since the data format changed, clean everything.
-            SharedPreferences.Editor edit = mPrefs.edit();
-            edit.clear();
-            edit.putInt(VERSION_PREF, CURRENT_VERSION);
-            edit.apply();
-        }
-    }
-
-    @VisibleForTesting
-    protected IPackageManager getPackageManager() {
-        return AppGlobals.getPackageManager();
-    }
-
-    // Returns a resolved app info for a given app info, or null if the app info is unlauncheable.
-    public ResolvedApp resolveApp(AppInfo appInfo) {
-        ComponentName component = appInfo.getComponentName();
-        int appUserId = appInfo.getUser().getIdentifier();
-
-        if (mCurrentUserId != appUserId) {
-            // Check if app user is a profile of current user and the app user is enabled.
-            UserInfo appUserInfo = mUserManager.getUserInfo(appUserId);
-            UserInfo currentUserInfo = mUserManager.getUserInfo(mCurrentUserId);
-            if (appUserInfo == null || currentUserInfo == null
-                    || appUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
-                    || appUserInfo.profileGroupId != currentUserInfo.profileGroupId
-                    || !appUserInfo.isEnabled()) {
-                Slog.e(TAG, "User " + appUserId +
-                        " is is not a profile of the current user, or is disabled.");
-                return null;
-            }
-        }
-
-        // This code is based on LauncherAppsService.startActivityAsUser code.
-        Intent launchIntent = new Intent(Intent.ACTION_MAIN);
-        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        launchIntent.setPackage(component.getPackageName());
-
-        try {
-            ActivityInfo info = getPackageManager().getActivityInfo(component, 0, appUserId);
-            if (info == null) {
-                Slog.e(TAG, "Activity " + component + " is not installed.");
-                return null;
-            }
-
-            if (!info.exported) {
-                Slog.e(TAG, "Activity " + component + " doesn't have 'exported' attribute.");
-                return null;
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to get activity info for " + component, e);
-            return null;
-        }
-
-        // Check that the component actually has Intent.CATEGORY_LAUNCHER
-        // as calling startActivityAsUser ignores the category and just
-        // resolves based on the component if present.
-        List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(launchIntent,
-                0 /* flags */, appUserId);
-        final int size = apps.size();
-        for (int i = 0; i < size; ++i) {
-            ResolveInfo ri = apps.get(i);
-            ActivityInfo activityInfo = ri.activityInfo;
-            if (activityInfo.packageName.equals(component.getPackageName()) &&
-                    activityInfo.name.equals(component.getClassName())) {
-                // Found an activity with category launcher that matches
-                // this component so ok to launch.
-                launchIntent.setComponent(component);
-                ResolvedApp resolvedApp = new ResolvedApp();
-                resolvedApp.launchIntent = launchIntent;
-                resolvedApp.ri = ri;
-                return resolvedApp;
-            }
-        }
-
-        Slog.i(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component);
-        return null;
-    }
-
-    public void addOnAppsChangedListener(OnAppsChangedListener listener) {
-        mOnAppsChangedListeners.add(listener);
-    }
-
-    public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
-        mOnAppsChangedListeners.remove(listener);
-    }
-
-    /**
-     * Reinitializes the model for a new user.
-     */
-    public void setCurrentUser(int userId) {
-        mCurrentUserId = userId;
-        mCurrentUserSerialNumber = mUserManager.getSerialNumberForUser(new UserHandle(userId));
-
-        mApps.clear();
-
-        int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1);
-        if (appCount >= 0) {
-            loadAppsFromPrefs(appCount);
-        } else {
-            // We switched to this user for the first time ever. This is a good opportunity to clean
-            // prefs for users deleted in the past.
-            removePrefsForDeletedUsers();
-
-            addDefaultApps();
-        }
-    }
-
-    /**
-     * Removes prefs for users that don't exist on the device.
-     */
-    private void removePrefsForDeletedUsers() {
-        // Build a set of string representations of serial numbers of the device users.
-        final List<UserInfo> users = mUserManager.getUsers();
-        final int userCount = users.size();
-
-        final Set<String> userSerials = new HashSet<String> ();
-
-        for (int i = 0; i < userCount; ++i) {
-            userSerials.add(Long.toString(users.get(i).serialNumber));
-        }
-
-        // Walk though all prefs and delete ones which user is not in the string set.
-        final Map<String, ?> allPrefs = mPrefs.getAll();
-        final SharedPreferences.Editor edit = mPrefs.edit();
-
-        for (Map.Entry<String, ?> pref : allPrefs.entrySet()) {
-            final String key = pref.getKey();
-            if (key.equals(VERSION_PREF)) continue;
-
-            final int userSeparatorPos = key.indexOf(USER_SEPARATOR);
-
-            if (userSeparatorPos < 0) {
-                // Removing anomalous pref with no user.
-                edit.remove(key);
-                continue;
-            }
-
-            final String prefUserSerial = key.substring(0, userSeparatorPos);
-
-            if (!userSerials.contains(prefUserSerial)) {
-                // Removes pref for a not existing user.
-                edit.remove(key);
-                continue;
-            }
-        }
-
-        edit.apply();
-    }
-
-    /** Returns the list of apps. */
-    public List<AppInfo> getApps() {
-        return mApps;
-    }
-
-    /** Sets the list of apps and saves it. */
-    public void setApps(List<AppInfo> apps) {
-        mApps = apps;
-        savePrefs();
-
-        int size = mOnAppsChangedListeners.size();
-        for (int i = 0; i < size; ++i) {
-            mOnAppsChangedListeners.get(i).onPinnedAppsChanged();
-        }
-    }
-
-    /** Saves the current model to disk. */
-    private void savePrefs() {
-        SharedPreferences.Editor edit = mPrefs.edit();
-        int appCount = mApps.size();
-        edit.putInt(userPrefixed(APP_COUNT_PREF), appCount);
-        for (int i = 0; i < appCount; i++) {
-            final AppInfo appInfo = mApps.get(i);
-            String componentNameString = appInfo.getComponentName().flattenToString();
-            edit.putString(prefNameForApp(i), componentNameString);
-            long userSerialNumber = mUserManager.getSerialNumberForUser(appInfo.getUser());
-            edit.putLong(prefUserForApp(i), userSerialNumber);
-        }
-        // Start an asynchronous disk write.
-        edit.apply();
-    }
-
-    /** Loads AppInfo from prefs. Returns null if something is wrong. */
-    private AppInfo loadAppFromPrefs(int index) {
-        String prefValue = mPrefs.getString(prefNameForApp(index), null);
-        if (prefValue == null) {
-            Slog.w(TAG, "Couldn't find pref " + prefNameForApp(index));
-            return null;
-        }
-        ComponentName componentName = ComponentName.unflattenFromString(prefValue);
-        if (componentName == null) {
-            Slog.w(TAG, "Invalid component name " + prefValue);
-            return null;
-        }
-        long userSerialNumber = mPrefs.getLong(prefUserForApp(index), -1);
-        if (userSerialNumber == -1) {
-            Slog.w(TAG, "Couldn't find pref " + prefUserForApp(index));
-            return null;
-        }
-        UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
-        if (appUser == null) {
-            Slog.w(TAG, "No user for serial " + userSerialNumber);
-            return null;
-        }
-        AppInfo appInfo = new AppInfo(componentName, appUser);
-        if (resolveApp(appInfo) == null) {
-            return null;
-        }
-        return appInfo;
-    }
-
-    /** Loads the list of apps from SharedPreferences. */
-    private void loadAppsFromPrefs(int appCount) {
-        for (int i = 0; i < appCount; i++) {
-            AppInfo appInfo = loadAppFromPrefs(i);
-            if (appInfo != null) {
-                mApps.add(appInfo);
-            }
-        }
-
-        if (appCount != mApps.size()) savePrefs();
-    }
-
-    /** Adds the first few apps from the owner profile. Used for demo purposes. */
-    private void addDefaultApps() {
-        // Get a list of all app activities.
-        final Intent queryIntent = new Intent(Intent.ACTION_MAIN, null);
-        queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
-        final List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(
-                queryIntent, 0 /* flags */, mCurrentUserId);
-        final int appCount = apps.size();
-        for (int i = 0; i < NUM_INITIAL_APPS && i < appCount; i++) {
-            ResolveInfo ri = apps.get(i);
-            ComponentName componentName = new ComponentName(
-                    ri.activityInfo.packageName, ri.activityInfo.name);
-            mApps.add(new AppInfo(componentName, new UserHandle(mCurrentUserId)));
-        }
-
-        savePrefs();
-    }
-
-    /** Returns a pref prefixed with the serial number of the current user. */
-    private String userPrefixed(String pref) {
-        return Long.toString(mCurrentUserSerialNumber) + USER_SEPARATOR + pref;
-    }
-
-    /** Returns the pref name for the app at a given index. */
-    private String prefNameForApp(int index) {
-        return userPrefixed(APP_PREF_PREFIX + Integer.toString(index));
-    }
-
-    /** Returns the pref name for the app's user at a given index. */
-    private String prefUserForApp(int index) {
-        return userPrefixed(APP_USER_PREFIX + Integer.toString(index));
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index beeee0b..7395a33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -43,8 +43,6 @@
     private static final String RECENT = "recent";
     private static final String NAVSPACE = "space";
 
-    private static final String APP_SHELF = "app_shelf";
-
     public static final String GRAVITY_SEPARATOR = ";";
     public static final String BUTTON_SEPARATOR = ",";
 
@@ -141,19 +139,13 @@
         inflateButtons(start, (ViewGroup) mRot90.findViewById(R.id.ends_group),
                 (ViewGroup) mRot90.findViewById(R.id.ends_group_lightsout), true);
 
-        if (center.length == 1 && APP_SHELF.equals(center[0])) {
-            inflateShelf((LinearLayout) mRot0.findViewById(R.id.ends_group),
-                    (LinearLayout) mRot0.findViewById(R.id.ends_group_lightsout), false);
-            inflateShelf((LinearLayout) mRot90.findViewById(R.id.ends_group),
-                    (LinearLayout) mRot90.findViewById(R.id.ends_group_lightsout), true);
-        } else {
-            inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group),
-                    (ViewGroup) mRot0.findViewById(R.id.center_group_lightsout), false);
-            inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group),
-                    (ViewGroup) mRot90.findViewById(R.id.center_group_lightsout), true);
-            addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group));
-            addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group));
-        }
+        inflateButtons(center, (ViewGroup) mRot0.findViewById(R.id.center_group),
+                (ViewGroup) mRot0.findViewById(R.id.center_group_lightsout), false);
+        inflateButtons(center, (ViewGroup) mRot90.findViewById(R.id.center_group),
+                (ViewGroup) mRot90.findViewById(R.id.center_group_lightsout), true);
+
+        addGravitySpacer((LinearLayout) mRot0.findViewById(R.id.ends_group));
+        addGravitySpacer((LinearLayout) mRot90.findViewById(R.id.ends_group));
 
         inflateButtons(end, (ViewGroup) mRot0.findViewById(R.id.ends_group),
                 (ViewGroup) mRot0.findViewById(R.id.ends_group_lightsout), false);
@@ -161,14 +153,6 @@
                 (ViewGroup) mRot90.findViewById(R.id.ends_group_lightsout), true);
     }
 
-    private void inflateShelf(LinearLayout layout, LinearLayout lightsOut, boolean landscape) {
-        View v = (landscape ? mLandscapeInflater : mLayoutInflater)
-                .inflate(R.layout.apps_bar, layout, false);
-        layout.addView(v);
-        addToDispatchers(v);
-        copyToLightsout(v, lightsOut);
-    }
-
     private void addGravitySpacer(LinearLayout layout) {
         layout.addView(new Space(mContext), new LinearLayout.LayoutParams(0, 0, 1));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 839b579..d86629f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -263,11 +263,6 @@
         return mButtonDisatchers.get(R.id.ime_switcher);
     }
 
-    @Nullable
-    public View getAppShelf() {
-        return getCurrentView().findViewById(R.id.app_shelf);
-    }
-
     private void getCarModeIcons(Context ctx) {
         mBackCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_carmode);
         mBackLandCarModeIcon = mBackCarModeIcon;
@@ -395,12 +390,6 @@
         getBackButton().setVisibility(disableBack      ? View.INVISIBLE : View.VISIBLE);
         getHomeButton().setVisibility(disableHome      ? View.INVISIBLE : View.VISIBLE);
         getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
-
-        // The app shelf, if it exists, follows the visibility of the home button.
-        View appShelf = getAppShelf();
-        if (appShelf != null) {
-            appShelf.setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
-        }
     }
 
     private boolean inLockTask() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index ba20679..8e89efd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,11 +39,11 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 import android.widget.TextView;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.DejankUtils;
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
@@ -104,7 +105,6 @@
     private View mQsNavbarScrim;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private NotificationStackScrollLayout mNotificationStackScroller;
-    private int mNotificationTopPadding;
     private boolean mAnimateNextTopPaddingChange;
 
     private int mTrackingPointer;
@@ -151,9 +151,6 @@
     private int mUnlockMoveDistance;
     private float mEmptyDragAmount;
 
-    private Interpolator mFastOutSlowInInterpolator;
-    private Interpolator mFastOutLinearInterpolator;
-    private Interpolator mDozeAnimationInterpolator;
     private ObjectAnimator mClockAnimator;
     private int mClockAnimationTarget = -1;
     private int mTopPaddingAdjustment;
@@ -253,12 +250,6 @@
         mNotificationStackScroller.setOverscrollTopChangedListener(this);
         mNotificationStackScroller.setOnEmptySpaceClickListener(this);
         mNotificationStackScroller.setScrollView(mScrollView);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.fast_out_slow_in);
-        mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.fast_out_linear_in);
-        mDozeAnimationInterpolator = AnimationUtils.loadInterpolator(getContext(),
-                android.R.interpolator.linear_out_slow_in);
         mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
         mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
         mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
@@ -281,8 +272,6 @@
     @Override
     protected void loadDimens() {
         super.loadDimens();
-        mNotificationTopPadding = getResources().getDimensionPixelSize(
-                R.dimen.notifications_top_padding);
         mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
         mStatusBarMinHeight = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
@@ -371,7 +360,7 @@
         }
         mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
         mQsSizeChangeAnimator.setDuration(300);
-        mQsSizeChangeAnimator.setInterpolator(mFastOutSlowInInterpolator);
+        mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
@@ -400,8 +389,8 @@
         if (mStatusBarState != StatusBarState.KEYGUARD) {
             int bottom = mHeader.getCollapsedHeight();
             stackScrollerPadding = mStatusBarState == StatusBarState.SHADE
-                    ? bottom + mQsPeekHeight + mNotificationTopPadding
-                    : mKeyguardStatusBar.getHeight() + mNotificationTopPadding;
+                    ? bottom + mQsPeekHeight
+                    : mKeyguardStatusBar.getHeight();
             mTopPaddingAdjustment = 0;
         } else {
             mClockPositionAlgorithm.setup(
@@ -433,8 +422,8 @@
     public int computeMaxKeyguardNotifications(int maximum) {
         float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(getHeight(),
                 mKeyguardStatusView.getHeight());
-        int keyguardPadding = getResources().getDimensionPixelSize(
-                R.dimen.notification_padding_dimmed);
+        int notificationPadding = Math.max(1, getResources().getDimensionPixelSize(
+                R.dimen.notification_divider_height));
         final int overflowheight = getResources().getDimensionPixelSize(
                 R.dimen.notification_summary_height);
         float bottomStackSize = mNotificationStackScroller.getKeyguardBottomStackSize();
@@ -446,7 +435,7 @@
             if (!(child instanceof ExpandableNotificationRow)) {
                 continue;
             }
-            availableSpace -= child.getMinHeight() + keyguardPadding;
+            availableSpace -= child.getMinHeight() + notificationPadding;
             if (availableSpace >= 0 && count < maximum) {
                 count++;
             } else {
@@ -471,7 +460,7 @@
                 }
                 mClockAnimator = ObjectAnimator
                         .ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget);
-                mClockAnimator.setInterpolator(mFastOutSlowInInterpolator);
+                mClockAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
                 mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
                 mClockAnimator.addListener(new AnimatorListenerAdapter() {
                     @Override
@@ -1107,7 +1096,7 @@
                     .translationY(0f)
                     .setStartDelay(delay)
                     .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
-                    .setInterpolator(mFastOutSlowInInterpolator)
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                     .start();
             mQsContainer.setY(-mQsContainer.getHeight());
             mQsContainerAnimator = ObjectAnimator.ofFloat(mQsContainer, View.TRANSLATION_Y,
@@ -1116,7 +1105,7 @@
                             - mQsContainer.getTop());
             mQsContainerAnimator.setStartDelay(delay);
             mQsContainerAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
-            mQsContainerAnimator.setInterpolator(mFastOutSlowInInterpolator);
+            mQsContainerAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
             mQsContainerAnimator.addListener(mAnimateHeaderSlidingInListener);
             mQsContainerAnimator.start();
             mQsContainer.addOnLayoutChangeListener(mQsContainerAnimatorUpdater);
@@ -1138,7 +1127,7 @@
         mHeader.animate().y(-mHeader.getHeight())
                 .setStartDelay(0)
                 .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
-                .setInterpolator(mFastOutSlowInInterpolator)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setListener(new AnimatorListenerAdapter() {
                     @Override
                     public void onAnimationEnd(Animator animation) {
@@ -1152,7 +1141,7 @@
                 .y(-mQsContainer.getHeight())
                 .setStartDelay(0)
                 .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
-                .setInterpolator(mFastOutSlowInInterpolator)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .start();
     }
 
@@ -1174,7 +1163,7 @@
         anim.setDuration(mStatusBar.isKeyguardFadingAway()
                 ? mStatusBar.getKeyguardFadingAwayDuration() / 2
                 : StackStateAnimator.ANIMATION_DURATION_STANDARD);
-        anim.setInterpolator(mDozeAnimationInterpolator);
+        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -1199,7 +1188,7 @@
         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
         anim.addUpdateListener(mStatusBarAnimateAlphaListener);
         anim.setDuration(duration);
-        anim.setInterpolator(mDozeAnimationInterpolator);
+        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
         anim.start();
     }
 
@@ -1218,7 +1207,7 @@
                     .alpha(0f)
                     .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
                     .setDuration(mStatusBar.getKeyguardFadingAwayDuration() / 2)
-                    .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
                     .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
                     .start();
         } else if (statusBarState == StatusBarState.KEYGUARD
@@ -1245,7 +1234,7 @@
                     .alpha(0f)
                     .setStartDelay(0)
                     .setDuration(160)
-                    .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
                     .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable);
             if (keyguardFadingAway) {
                 mKeyguardStatusView.animate()
@@ -1263,7 +1252,7 @@
                     .alpha(1f)
                     .setStartDelay(0)
                     .setDuration(320)
-                    .setInterpolator(PhoneStatusBar.ALPHA_IN)
+                    .setInterpolator(Interpolators.ALPHA_IN)
                     .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
         } else if (statusBarState == StatusBarState.KEYGUARD) {
             mKeyguardStatusView.animate().cancel();
@@ -1380,8 +1369,7 @@
             // on Keyguard, maxQs denotes the top padding from the quick settings panel. We need to
             // take the maximum and linearly interpolate with the panel expansion for a nice motion.
             int maxNotifications = mClockPositionResult.stackScrollerPadding
-                    - mClockPositionResult.stackScrollerPaddingAdjustment
-                    - mNotificationTopPadding;
+                    - mClockPositionResult.stackScrollerPaddingAdjustment;
             int maxQs = getTempQsMaxExpansion();
             int max = mStatusBarState == StatusBarState.KEYGUARD
                     ? Math.max(maxNotifications, maxQs)
@@ -1395,7 +1383,7 @@
             // We can only do the smoother transition on Keyguard when we also are not collapsing
             // from a scrolled quick settings.
             return interpolate(getQsExpansionFraction(),
-                    mNotificationStackScroller.getIntrinsicPadding() - mNotificationTopPadding,
+                    mNotificationStackScroller.getIntrinsicPadding(),
                     mQsMaxExpansionHeight);
         } else {
             return mQsExpansionHeight;
@@ -1625,15 +1613,13 @@
             maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
         }
         float totalHeight = Math.max(
-                maxQsHeight + mNotificationStackScroller.getNotificationTopPadding(),
-                mStatusBarState == StatusBarState.KEYGUARD
+                maxQsHeight, mStatusBarState == StatusBarState.KEYGUARD
                         ? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment
                         : 0)
                 + notificationHeight;
         if (totalHeight > mNotificationStackScroller.getHeight()) {
             float fullyCollapsedHeight = maxQsHeight
                     + mNotificationStackScroller.getMinStackHeight()
-                    + mNotificationStackScroller.getNotificationTopPadding()
                     - getScrollViewScrollY();
             totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
         }
@@ -1680,14 +1666,14 @@
             boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance;
             KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
             if (active && !mUnlockIconActive && mTracking) {
-                lockIcon.setImageAlpha(1.0f, true, 150, mFastOutLinearInterpolator, null);
+                lockIcon.setImageAlpha(1.0f, true, 150, Interpolators.FAST_OUT_LINEAR_IN, null);
                 lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
-                        mFastOutLinearInterpolator);
+                        Interpolators.FAST_OUT_LINEAR_IN);
             } else if (!active && mUnlockIconActive && mTracking) {
                 lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */,
-                        150, mFastOutLinearInterpolator, null);
+                        150, Interpolators.FAST_OUT_LINEAR_IN, null);
                 lockIcon.setImageScale(1.0f, true, 150,
-                        mFastOutLinearInterpolator);
+                        Interpolators.FAST_OUT_LINEAR_IN);
             }
             mUnlockIconActive = active;
         }
@@ -1727,7 +1713,7 @@
         float translation = stackTranslation / HEADER_RUBBERBAND_FACTOR;
         if (mHeadsUpManager.hasPinnedHeadsUp() || mIsExpansionFromHeadsUp) {
             translation = mNotificationStackScroller.getTopPadding() + stackTranslation
-                    - mNotificationTopPadding - mQsMinExpansionHeight;
+                    - mQsMinExpansionHeight;
         }
         return Math.min(0, translation);
     }
@@ -1892,8 +1878,8 @@
         if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
                 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
             KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
-            lockIcon.setImageAlpha(0.0f, true, 100, mFastOutLinearInterpolator, null);
-            lockIcon.setImageScale(2.0f, true, 100, mFastOutLinearInterpolator);
+            lockIcon.setImageAlpha(0.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN, null);
+            lockIcon.setImageScale(2.0f, true, 100, Interpolators.FAST_OUT_LINEAR_IN);
         }
     }
 
@@ -2033,12 +2019,12 @@
      */
     private void startHighlightIconAnimation(final KeyguardAffordanceView icon) {
         icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
-                mFastOutSlowInInterpolator, new Runnable() {
+                Interpolators.FAST_OUT_SLOW_IN, new Runnable() {
                     @Override
                     public void run() {
                         icon.setImageAlpha(icon.getRestingAlpha(),
                                 true /* animate */, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
-                                mFastOutSlowInInterpolator, null);
+                                Interpolators.FAST_OUT_SLOW_IN, null);
                     }
                 });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index f0b7894..f036d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -29,7 +29,6 @@
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 import android.view.ViewTreeObserver;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
@@ -39,6 +38,7 @@
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -100,8 +100,6 @@
     private float mInitialTouchX;
     private boolean mTouchDisabled;
 
-    private Interpolator mLinearOutSlowInInterpolator;
-    private Interpolator mFastOutSlowInInterpolator;
     private Interpolator mBounceInterpolator;
     protected KeyguardBottomAreaView mKeyguardBottomArea;
 
@@ -161,7 +159,7 @@
         }
         mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight)
                 .setDuration(250);
-        mPeekAnimator.setInterpolator(mLinearOutSlowInInterpolator);
+        mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
         mPeekAnimator.addListener(new AnimatorListenerAdapter() {
             private boolean mCancelled;
 
@@ -187,10 +185,6 @@
     public PanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f);
-        mFastOutSlowInInterpolator =
-                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
-        mLinearOutSlowInInterpolator =
-                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
         mBounceInterpolator = new BounceInterpolator();
         mFalsingManager = FalsingManager.getInstance(context);
     }
@@ -951,7 +945,7 @@
         float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
         ValueAnimator animator = createHeightAnimator(target);
         animator.setDuration(250);
-        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         animator.addListener(new AnimatorListenerAdapter() {
             private boolean mCancelled;
 
@@ -975,7 +969,7 @@
         mKeyguardBottomArea.getIndicationView().animate()
                 .translationY(-mHintDistance)
                 .setDuration(250)
-                .setInterpolator(mFastOutSlowInInterpolator)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e2a6184..354d704 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -87,13 +87,11 @@
 import android.view.ViewStub;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
-import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.PathInterpolator;
 import android.widget.ImageView;
 import android.widget.TextView;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.statusbar.StatusBarIcon;
@@ -124,6 +122,7 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
@@ -131,7 +130,6 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.SignalClusterView;
-import com.android.systemui.statusbar.SpeedBumpView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -438,10 +436,8 @@
     private boolean mDozingRequested;
     protected boolean mScrimSrcModeEnabled;
 
-    private Interpolator mLinearInterpolator = new LinearInterpolator();
-    private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
-    public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
-    public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+    public static final Interpolator ALPHA_IN = Interpolators.ALPHA_IN;
+    public static final Interpolator ALPHA_OUT = Interpolators.ALPHA_OUT;
 
     private BackDropView mBackdrop;
     private ImageView mBackdropFront, mBackdropBack;
@@ -744,9 +740,7 @@
         mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
         mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer);
 
-        SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
-                        R.layout.status_bar_notification_speed_bump, mStackScroller, false);
-        mStackScroller.setSpeedBumpView(speedBump);
+
         mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
                 R.layout.status_bar_no_notifications, mStackScroller, false);
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
@@ -1256,7 +1250,7 @@
         }
 
         if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
-            if (mNotificationData.shouldSuppressScreenOn(notification.getKey())) {
+            if (shouldSupressFullScreenIntent(notification.getKey())) {
                 if (DEBUG) {
                     Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
                 }
@@ -1283,6 +1277,14 @@
         setAreThereNotifications();
     }
 
+    private boolean shouldSupressFullScreenIntent(String key) {
+        if (mPowerManager.isInteractive()) {
+            return mNotificationData.shouldSuppressPeek(key);
+        } else {
+            return mNotificationData.shouldSuppressScreenOn(key);
+        }
+    }
+
     @Override
     protected void updateNotificationRanking(RankingMap ranking) {
         mNotificationData.updateRanking(ranking);
@@ -1841,7 +1843,7 @@
                             // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
                             // libhwui.
                             .alpha(0.002f)
-                            .setInterpolator(mBackdropInterpolator)
+                            .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
                             .setDuration(300)
                             .setStartDelay(0)
                             .withEndAction(new Runnable() {
@@ -1860,7 +1862,7 @@
                                 // behind.
                                 .setDuration(mKeyguardFadingAwayDuration / 2)
                                 .setStartDelay(mKeyguardFadingAwayDelay)
-                                .setInterpolator(mLinearInterpolator)
+                                .setInterpolator(Interpolators.LINEAR)
                                 .start();
                     }
                 }
@@ -3740,7 +3742,7 @@
         mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
         updateDozingState();
         updatePublicMode();
-        updateStackScrollerState(goingToFullShade);
+        updateStackScrollerState(goingToFullShade, fromShadeLocked);
         updateNotifications();
         checkBarModes();
         updateMediaMetaData(false);
@@ -3761,11 +3763,11 @@
                         != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
     }
 
-    public void updateStackScrollerState(boolean goingToFullShade) {
+    public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
         if (mStackScroller == null) return;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
         mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
-        mStackScroller.setDimmed(onKeyguard, false /* animate */);
+        mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
         mStackScroller.setExpandingEnabled(!onKeyguard);
         ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
         mStackScroller.setActivatedChild(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 7e27856..9c2159be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -38,6 +38,7 @@
 import com.android.systemui.qs.tiles.CastTile;
 import com.android.systemui.qs.tiles.CellularTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
+import com.android.systemui.qs.tiles.DataSaverTile;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.FlashlightTile;
 import com.android.systemui.qs.tiles.HotspotTile;
@@ -346,6 +347,7 @@
         else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
         else if (tileSpec.equals("user")) return new UserTile(this);
         else if (tileSpec.equals("battery")) return new BatteryTile(this);
+        else if (tileSpec.equals("saver")) return new DataSaverTile(this);
         else if (tileSpec.equals(ColorMatrixTile.COLOR_MATRIX_SPEC))
             return new ColorMatrixTile(this);
         // Intent tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 5b44f0a..f18c341 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.View;
@@ -70,6 +71,8 @@
     private QuickQSPanel mHeaderQsPanel;
     private boolean mShowEmergencyCallsOnly;
     private float mDateTimeTranslation;
+    private MultiUserSwitch mMultiUserSwitch;
+    private ImageView mMultiUserAvatar;
 
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -100,6 +103,9 @@
         mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
         mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
 
+        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
+        mMultiUserAvatar = (ImageView) mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
+
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
         ((RippleDrawable) getBackground()).setForceSoftware(true);
@@ -173,6 +179,12 @@
                 ? View.VISIBLE : View.INVISIBLE);
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
                 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
+        mMultiUserSwitch.setVisibility(mMultiUserSwitch.hasMultipleUsers() ? View.VISIBLE
+                : View.GONE);
+    }
+
+    private boolean hasMultiUsers() {
+        return false;
     }
 
     private void updateListeners() {
@@ -194,6 +206,7 @@
         setupHost(qsPanel.getHost());
         if (mQsPanel != null) {
             mQsPanel.setCallback(mQsPanelCallback);
+            mMultiUserSwitch.setQsPanel(qsPanel);
         }
     }
 
@@ -254,7 +267,12 @@
 
     @Override
     public void setUserInfoController(UserInfoController userInfoController) {
-        // Don't care.
+        userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
+            @Override
+            public void onUserInfoChanged(String name, Drawable picture) {
+                mMultiUserAvatar.setImageDrawable(picture);
+            }
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 403199a..f310c2c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -22,6 +22,7 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.animation.DecelerateInterpolator;
@@ -497,4 +498,16 @@
     public void dontAnimateBouncerChangesUntilNextFrame() {
         mDontAnimateBouncerChanges = true;
     }
+
+    public void setExcludedBackgroundArea(Rect area) {
+        mScrimBehind.setExcludedArea(area);
+    }
+
+    public int getScrimBehindColor() {
+        return mScrimBehind.getScrimColorWithAlpha();
+    }
+
+    public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
+        mScrimBehind.setChangeRunnable(changeRunnable);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 512af1b..7247b57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -26,7 +26,9 @@
 import android.view.ViewConfiguration;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
+
 import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.statusbar.Interpolators;
 
 public class SettingsButton extends AlphaOptimizedImageButton {
 
@@ -157,8 +159,7 @@
         performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         mUpToSpeed = true;
         mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360);
-        mAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.linear));
+        mAnimator.setInterpolator(Interpolators.LINEAR);
         mAnimator.setDuration(FULL_SPEED_LENGTH);
         mAnimator.setRepeatCount(Animation.INFINITE);
         mAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 64fb066..c6537e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -32,15 +32,15 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -63,8 +63,6 @@
 
     private Context mContext;
     private PhoneStatusBar mPhoneStatusBar;
-    private Interpolator mLinearOutSlowIn;
-    private Interpolator mFastOutSlowIn;
     private DemoStatusIcons mDemoStatusIcons;
 
     private LinearLayout mSystemIconArea;
@@ -129,10 +127,6 @@
         maybeScaleBatteryMeterView(context);
 
         mClock = (TextView) statusBar.findViewById(R.id.clock);
-        mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.linear_out_slow_in);
-        mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
-                android.R.interpolator.fast_out_slow_in);
         mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
         mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
         mHandler = new Handler();
@@ -348,7 +342,7 @@
                 .alpha(0f)
                 .setDuration(160)
                 .setStartDelay(0)
-                .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                .setInterpolator(Interpolators.ALPHA_OUT)
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
@@ -370,7 +364,7 @@
         v.animate()
                 .alpha(1f)
                 .setDuration(320)
-                .setInterpolator(PhoneStatusBar.ALPHA_IN)
+                .setInterpolator(Interpolators.ALPHA_IN)
                 .setStartDelay(50)
 
                 // We need to clean up any pending end action from animateHide if we call
@@ -382,7 +376,7 @@
         if (mPhoneStatusBar.isKeyguardFadingAway()) {
             v.animate()
                     .setDuration(mPhoneStatusBar.getKeyguardFadingAwayDuration())
-                    .setInterpolator(mLinearOutSlowIn)
+                    .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
                     .setStartDelay(mPhoneStatusBar.getKeyguardFadingAwayDelay())
                     .start();
         }
@@ -419,7 +413,7 @@
         });
         mTintAnimator.setDuration(duration);
         mTintAnimator.setStartDelay(delay);
-        mTintAnimator.setInterpolator(mFastOutSlowIn);
+        mTintAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         mTintAnimator.start();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
index 56c1e10..1aae496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import com.android.systemui.R;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -31,10 +29,11 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
+import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
+
 public class TrustDrawable extends Drawable {
 
     private static final long ENTERING_FROM_UNSET_START_DELAY = 200;
@@ -69,10 +68,6 @@
 
     private final Animator mVisibleAnimator;
 
-    private final Interpolator mLinearOutSlowInInterpolator;
-    private final Interpolator mFastOutSlowInInterpolator;
-    private final Interpolator mAccelerateDecelerateInterpolator;
-
     public TrustDrawable(Context context) {
         Resources r = context.getResources();
         mInnerRadiusVisibleMin = r.getDimension(R.dimen.trust_circle_inner_radius_visible_min);
@@ -83,12 +78,6 @@
 
         mCurInnerRadius = mInnerRadiusEnter;
 
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
-                context, android.R.interpolator.linear_out_slow_in);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
-                context, android.R.interpolator.fast_out_slow_in);
-        mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
-
         mVisibleAnimator = makeVisibleAnimator();
 
         mPaint = new Paint();
@@ -212,19 +201,19 @@
     private Animator makeVisibleAnimator() {
         return makeAnimators(mInnerRadiusVisibleMax, mInnerRadiusVisibleMin,
                 ALPHA_VISIBLE_MAX, ALPHA_VISIBLE_MIN, VISIBLE_DURATION,
-                mAccelerateDecelerateInterpolator,
+                Interpolators.ACCELERATE_DECELERATE,
                 true /* repeating */, false /* stateUpdateListener */);
     }
 
     private Animator makeEnterAnimator(float radius, int alpha) {
         return makeAnimators(radius, mInnerRadiusVisibleMax,
-                alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, mLinearOutSlowInInterpolator,
+                alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, Interpolators.LINEAR_OUT_SLOW_IN,
                 false /* repeating */, true /* stateUpdateListener */);
     }
 
     private Animator makeExitAnimator(float radius, int alpha) {
         return makeAnimators(radius, mInnerRadiusExit,
-                alpha, 0, EXIT_DURATION, mFastOutSlowInInterpolator,
+                alpha, 0, EXIT_DURATION, Interpolators.FAST_OUT_SLOW_IN,
                 false /* repeating */, true /* stateUpdateListener */);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 4ae0321..59d54ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -16,15 +16,15 @@
 
 package com.android.systemui.statusbar.policy;
 
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
-
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.widget.FrameLayout;
 
+import com.android.systemui.R;
+import com.android.systemui.statusbar.Interpolators;
+import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
+
 /**
  * Controls showing and hiding of the brightness mirror.
  */
@@ -46,13 +46,13 @@
 
     public void showMirror() {
         mBrightnessMirror.setVisibility(View.VISIBLE);
-        mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, PhoneStatusBar.ALPHA_OUT);
+        mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, Interpolators.ALPHA_OUT);
         outAnimation(mNotificationPanel.animate())
                 .withLayer();
     }
 
     public void hideMirror() {
-        mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, PhoneStatusBar.ALPHA_IN);
+        mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, Interpolators.ALPHA_IN);
         inAnimation(mNotificationPanel.animate())
                 .withLayer()
                 .withEndAction(new Runnable() {
@@ -66,12 +66,12 @@
     private ViewPropertyAnimator outAnimation(ViewPropertyAnimator a) {
         return a.alpha(0.0f)
                 .setDuration(TRANSITION_DURATION_OUT)
-                .setInterpolator(PhoneStatusBar.ALPHA_OUT);
+                .setInterpolator(Interpolators.ALPHA_OUT);
     }
     private ViewPropertyAnimator inAnimation(ViewPropertyAnimator a) {
         return a.alpha(1.0f)
                 .setDuration(TRANSITION_DURATION_IN)
-                .setInterpolator(PhoneStatusBar.ALPHA_IN);
+                .setInterpolator(Interpolators.ALPHA_IN);
     }
 
 
@@ -103,10 +103,5 @@
         lp.gravity = mBrightnessMirror.getResources().getInteger(
                 R.integer.notification_panel_layout_gravity);
         mBrightnessMirror.setLayoutParams(lp);
-
-        int padding = mBrightnessMirror.getResources().getDimensionPixelSize(
-                R.dimen.notification_side_padding);
-        mBrightnessMirror.setPadding(padding, mBrightnessMirror.getPaddingTop(),
-                padding, mBrightnessMirror.getPaddingBottom());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
new file mode 100644
index 0000000..186e8b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.net.INetworkPolicyListener;
+import android.net.NetworkPolicyManager;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+
+public class DataSaverController {
+
+    private final Handler mHandler = new Handler();
+    private final ArrayList<Listener> mListeners = new ArrayList<>();
+    private final NetworkPolicyManager mPolicyManager;
+
+    public DataSaverController(Context context) {
+        mPolicyManager = NetworkPolicyManager.from(context);
+    }
+
+    private void handleRestrictBackgroundChanged(boolean isDataSaving) {
+        final int N = mListeners.size();
+        for (int i = 0; i < N; i++) {
+            mListeners.get(i).onDataSaverChanged(isDataSaving);
+        }
+    }
+
+    public void addListener(Listener listener) {
+        mListeners.add(listener);
+        if (mListeners.size() == 1) {
+            mPolicyManager.registerListener(mPolicyListener);
+        }
+        listener.onDataSaverChanged(isDataSaverEnabled());
+    }
+
+    public void remListener(Listener listener) {
+        mListeners.remove(listener);
+        if (mListeners.size() == 0) {
+            mPolicyManager.unregisterListener(mPolicyListener);
+        }
+    }
+
+    public boolean isDataSaverEnabled() {
+        return mPolicyManager.getRestrictBackground();
+    }
+
+    public void setDataSaverEnabled(boolean enabled) {
+        mPolicyManager.setRestrictBackground(enabled);
+    }
+
+    private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+        @Override
+        public void onUidRulesChanged(int i, int i1) throws RemoteException {
+        }
+
+        @Override
+        public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
+        }
+
+        @Override
+        public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    handleRestrictBackgroundChanged(isDataSaving);
+                }
+            });
+        }
+    };
+
+    public interface Listener {
+        void onDataSaverChanged(boolean isDataSaving);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index dd7bd56..f065522 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -84,7 +84,6 @@
 
     private final View mStatusBarWindowView;
     private final int mStatusBarHeight;
-    private final int mNotificationsTopPadding;
     private final Context mContext;
     private final NotificationGroupManager mGroupManager;
     private PhoneStatusBar mBar;
@@ -138,8 +137,6 @@
         mGroupManager = groupManager;
         mStatusBarHeight = resources.getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
-        mNotificationsTopPadding = context.getResources()
-                .getDimensionPixelSize(R.dimen.notifications_top_padding);
     }
 
     private void updateTouchableRegionListener() {
@@ -399,7 +396,7 @@
             }
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(minX, 0, maxX, maxY + mNotificationsTopPadding);
+            info.touchableRegion.set(minX, 0, maxX, maxY);
         } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 41aeac9..5719f76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -20,8 +20,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
-import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.settingslib.TetherUtil;
@@ -34,21 +34,18 @@
 
     private static final String TAG = "HotspotController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final Intent TETHER_SERVICE_INTENT = new Intent()
-            .putExtra(TetherUtil.EXTRA_ADD_TETHER_TYPE, TetherUtil.TETHERING_WIFI)
-            .putExtra(TetherUtil.EXTRA_SET_ALARM, true)
-            .putExtra(TetherUtil.EXTRA_RUN_PROVISION, true)
-            .putExtra(TetherUtil.EXTRA_ENABLE_WIFI_TETHER, true)
-            .setComponent(TetherUtil.TETHER_SERVICE);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final Receiver mReceiver = new Receiver();
+    private final ConnectivityManager mConnectivityManager;
     private final Context mContext;
 
     private int mHotspotState;
 
     public HotspotControllerImpl(Context context) {
         mContext = context;
+        mConnectivityManager = (ConnectivityManager)context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -72,6 +69,7 @@
         return null;
     }
 
+    @Override
     public void addCallback(Callback callback) {
         if (callback == null || mCallbacks.contains(callback)) return;
         if (DEBUG) Log.d(TAG, "addCallback " + callback);
@@ -79,6 +77,7 @@
         mReceiver.setListening(!mCallbacks.isEmpty());
     }
 
+    @Override
     public void removeCallback(Callback callback) {
         if (callback == null) return;
         if (DEBUG) Log.d(TAG, "removeCallback " + callback);
@@ -96,13 +95,24 @@
         return TetherUtil.isTetheringSupported(mContext);
     }
 
+    static final class OnStartTetheringCallback extends
+            ConnectivityManager.OnStartTetheringCallback {
+        @Override
+        public void onTetheringStarted() {}
+        @Override
+        public void onTetheringFailed() {
+          // TODO: Show error.
+        }
+    }
+
     @Override
     public void setHotspotEnabled(boolean enabled) {
-        // Call provisioning app which is called when enabling Tethering from Settings
-        if (enabled && TetherUtil.isProvisioningNeeded(mContext)) {
-            mContext.startServiceAsUser(TETHER_SERVICE_INTENT, UserHandle.CURRENT);
+        if (enabled) {
+            OnStartTetheringCallback callback = new OnStartTetheringCallback();
+            mConnectivityManager.startTethering(
+                    ConnectivityManager.TETHERING_WIFI, false, callback);
         } else {
-            TetherUtil.setWifiTethering(enabled, mContext);
+            mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index 3f63b5f..d739d6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -32,7 +32,7 @@
 import android.view.animation.Interpolator;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.Interpolators;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -59,7 +59,6 @@
     private int mMaxWidth;
 
     private final Interpolator mInterpolator = new LogInterpolator();
-    private final Interpolator mAlphaExitInterpolator = PhoneStatusBar.ALPHA_OUT;
     private boolean mSupportHardware;
     private final View mTargetView;
 
@@ -225,7 +224,7 @@
 
     private void exitSoftware() {
         ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f);
-        alphaAnimator.setInterpolator(mAlphaExitInterpolator);
+        alphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
         alphaAnimator.setDuration(ANIMATION_DURATION_FADE);
         alphaAnimator.addListener(mAnimatorListener);
         alphaAnimator.start();
@@ -331,7 +330,7 @@
         final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
                 RenderNodeAnimator.PAINT_ALPHA, 0);
         opacityAnim.setDuration(ANIMATION_DURATION_FADE);
-        opacityAnim.setInterpolator(mAlphaExitInterpolator);
+        opacityAnim.setInterpolator(Interpolators.ALPHA_OUT);
         opacityAnim.addListener(mAnimatorListener);
         opacityAnim.setTarget(mTargetView);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 5cf6156..0959b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -21,6 +21,7 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.database.DataSetObserver;
+import android.graphics.Interpolator;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -33,9 +34,9 @@
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.systemui.R;
 import com.android.systemui.qs.tiles.UserDetailItemView;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 /**
  * Manages the user switcher on the Keyguard.
@@ -73,8 +74,7 @@
             mAdapter.registerDataSetObserver(mDataSetObserver);
             mUserSwitcherController = userSwitcherController;
             mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f,
-                    AnimationUtils.loadInterpolator(
-                            context, android.R.interpolator.fast_out_slow_in));
+                    Interpolators.FAST_OUT_SLOW_IN);
             mUserSwitcherContainer.setKeyguardUserSwitcher(this);
         } else {
             mUserSwitcherContainer = null;
@@ -158,7 +158,7 @@
         mAnimating = true;
         mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
         mBgAnimator.setDuration(400);
-        mBgAnimator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+        mBgAnimator.setInterpolator(Interpolators.ALPHA_IN);
         mBgAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -174,7 +174,7 @@
         mUserSwitcher.animate()
                 .alpha(0f)
                 .setDuration(300)
-                .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                .setInterpolator(Interpolators.ALPHA_OUT)
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index b2bcde3..755a5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -33,6 +33,7 @@
     void onUserSwitched(int newUserId);
     AccessPointController getAccessPointController();
     DataUsageController getMobileDataController();
+    DataSaverController getDataSaverController();
 
     public interface SignalCallback {
         void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 3385c82..107a904 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -78,6 +78,7 @@
     private final SubscriptionManager mSubscriptionManager;
     private final boolean mHasMobileDataFeature;
     private final SubscriptionDefaults mSubDefaults;
+    private final DataSaverController mDataSaverController;
     private Config mConfig;
 
     // Subcontrollers.
@@ -156,6 +157,7 @@
         mConfig = config;
         mReceiverHandler = new Handler(bgLooper);
         mCallbackHandler = callbackHandler;
+        mDataSaverController = new DataSaverController(context);
 
         mSubscriptionManager = subManager;
         mSubDefaults = defaultsHandler;
@@ -189,6 +191,10 @@
         updateAirplaneMode(true /* force callback */);
     }
 
+    public DataSaverController getDataSaverController() {
+        return mDataSaverController;
+    }
+
     private void registerListeners() {
         for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
             mobileSignalController.registerListener();
@@ -811,11 +817,11 @@
 
     public static class SubscriptionDefaults {
         public int getDefaultVoiceSubId() {
-            return SubscriptionManager.getDefaultVoiceSubId();
+            return SubscriptionManager.getDefaultVoiceSubscriptionId();
         }
 
         public int getDefaultDataSubId() {
-            return SubscriptionManager.getDefaultDataSubId();
+            return SubscriptionManager.getDefaultDataSubscriptionId();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 56f6564..561b18a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -25,12 +25,12 @@
     boolean animateAlpha;
     boolean animateY;
     boolean animateZ;
-    boolean animateScale;
     boolean animateHeight;
     boolean animateTopInset;
     boolean animateDimmed;
     boolean animateDark;
     boolean animateHideSensitive;
+    public boolean animateShadowAlpha;
     boolean hasDelays;
     boolean hasGoToFullShadeEvent;
     boolean hasDarkEvent;
@@ -57,11 +57,6 @@
         return this;
     }
 
-    public AnimationFilter animateScale() {
-        animateScale = true;
-        return this;
-    }
-
     public AnimationFilter animateHeight() {
         animateHeight = true;
         return this;
@@ -87,6 +82,11 @@
         return this;
     }
 
+    public AnimationFilter animateShadowAlpha() {
+        animateShadowAlpha = true;
+        return this;
+    }
+
     /**
      * Combines multiple filters into {@code this} filter, using or as the operand .
      *
@@ -118,12 +118,12 @@
         animateAlpha |= filter.animateAlpha;
         animateY |= filter.animateY;
         animateZ |= filter.animateZ;
-        animateScale |= filter.animateScale;
         animateHeight |= filter.animateHeight;
         animateTopInset |= filter.animateTopInset;
         animateDimmed |= filter.animateDimmed;
         animateDark |= filter.animateDark;
         animateHideSensitive |= filter.animateHideSensitive;
+        animateShadowAlpha |= filter.animateShadowAlpha;
         hasDelays |= filter.hasDelays;
     }
 
@@ -131,8 +131,8 @@
         animateAlpha = false;
         animateY = false;
         animateZ = false;
-        animateScale = false;
         animateHeight = false;
+        animateShadowAlpha = false;
         animateTopInset = false;
         animateDimmed = false;
         animateDark = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index dc40fb0..409dac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -76,8 +76,8 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         mChildPadding = getResources().getDimensionPixelSize(
                 R.dimen.notification_children_padding);
-        mDividerHeight = getResources().getDimensionPixelSize(
-                R.dimen.notification_children_divider_height);
+        mDividerHeight = Math.max(1, getResources().getDimensionPixelSize(
+                R.dimen.notification_divider_height));
         mMaxNotificationHeight = getResources().getDimensionPixelSize(
                 R.dimen.notification_max_height);
         mNotificationAppearDistance = getResources().getDimensionPixelSize(
@@ -324,7 +324,6 @@
             childState.dark = parentState.dark;
             childState.hideSensitive = parentState.hideSensitive;
             childState.belowSpeedBump = parentState.belowSpeedBump;
-            childState.scale =  1.0f;
             childState.clipTopAmount = 0;
             childState.topOverLap = 0;
             boolean visible = i <= lastVisibleIndex;
@@ -384,30 +383,8 @@
      * @param state the new state we animate to
      */
     public void prepareExpansionChanged(StackScrollState state) {
-        if (true) {
-            // TODO: do something that makes sense
-            return;
-        }
-        int childCount = mChildren.size();
-        StackViewState sourceState = new StackViewState();
-        ViewState dividerState = new ViewState();
-        for (int i = 0; i < childCount; i++) {
-            ExpandableNotificationRow child = mChildren.get(i);
-            StackViewState viewState = state.getViewStateForView(child);
-            sourceState.copyFrom(viewState);
-            sourceState.alpha = 0;
-            sourceState.yTranslation += mNotificationAppearDistance;
-            state.applyState(child, sourceState);
-
-            // layout the divider
-            View divider = mDividers.get(i);
-            dividerState.initFrom(divider);
-            dividerState.yTranslation = viewState.yTranslation - mDividerHeight
-                    + mNotificationAppearDistance;
-            dividerState.alpha = 0;
-            state.applyViewState(divider, dividerState);
-
-        }
+        // TODO: do something that makes sense, like placing the invisible views correctly
+        return;
     }
 
     public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
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 36b2810..d5b57ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -16,12 +16,22 @@
 
 package com.android.systemui.statusbar.stack;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeAnimator;
+import android.animation.ValueAnimator;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PointF;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
@@ -32,6 +42,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.OverScroller;
 
 import com.android.systemui.ExpandHelper;
@@ -43,9 +54,8 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.NotificationOverflowContainer;
-import com.android.systemui.statusbar.SpeedBumpView;
 import com.android.systemui.statusbar.StackScrollerDecorView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -64,6 +74,7 @@
         implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
         ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener {
 
+    public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
     private static final String TAG = "NotificationStackScrollLayout";
     private static final boolean DEBUG = false;
     private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
@@ -79,6 +90,7 @@
     private SwipeHelper mSwipeHelper;
     private boolean mSwipingInProgress;
     private int mCurrentStackHeight = Integer.MAX_VALUE;
+    private final Paint mBackgroundPaint = new Paint();
 
     /**
      * mCurrentStackHeight is the actual stack height, mLastSetStackHeight is the stack height set
@@ -103,15 +115,13 @@
     private float mInitialTouchX;
     private float mInitialTouchY;
 
-    private int mSidePaddings;
     private Paint mDebugPaint;
     private int mContentHeight;
     private int mCollapsedSize;
     private int mBottomStackSlowDownHeight;
     private int mBottomStackPeekSize;
     private int mPaddingBetweenElements;
-    private int mPaddingBetweenElementsDimmed;
-    private int mPaddingBetweenElementsNormal;
+    private int mIncreasedPaddingBetweenElements;
     private int mTopPadding;
     private int mCollapseSecondCardPadding;
 
@@ -163,7 +173,6 @@
     private boolean mGoToFullShadeNeedsAnimation;
     private boolean mIsExpanded = true;
     private boolean mChildrenUpdateRequested;
-    private SpeedBumpView mSpeedBumpView;
     private boolean mIsExpansionChanging;
     private boolean mPanelTracking;
     private boolean mExpandingNotification;
@@ -183,7 +192,6 @@
      */
     private float mMinTopOverScrollToEscape;
     private int mIntrinsicPadding;
-    private int mNotificationTopPadding;
     private float mStackTranslation;
     private float mTopPaddingOverflow;
     private boolean mDontReportNextOverScroll;
@@ -234,6 +242,45 @@
     private NotificationOverflowContainer mOverflowContainer;
     private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
     private FalsingManager mFalsingManager;
+    private boolean mAnimationRunning;
+    private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater
+            = new ViewTreeObserver.OnPreDrawListener() {
+        @Override
+        public boolean onPreDraw() {
+            // if it needs animation
+            if (!mNeedsAnimation && !mChildrenUpdateRequested) {
+                updateBackground();
+            }
+            return true;
+        }
+    };
+    private Rect mBackgroundBounds = new Rect();
+    private Rect mStartAnimationRect = new Rect();
+    private Rect mEndAnimationRect = new Rect();
+    private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
+    private boolean mAnimateNextBackgroundBottom;
+    private boolean mAnimateNextBackgroundTop;
+    private ObjectAnimator mBottomAnimator = null;
+    private ObjectAnimator mTopAnimator = null;
+    private ActivatableNotificationView mFirstVisibleBackgroundChild = null;
+    private ActivatableNotificationView mLastVisibleBackgroundChild = null;
+    private int mBgColor;
+    private float mDimAmount;
+    private ValueAnimator mDimAnimator;
+    private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mDimAnimator = null;
+        }
+    };
+    private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
+            = new ValueAnimator.AnimatorUpdateListener() {
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            setDimAmount((Float) animation.getAnimatedValue());
+        }
+    };
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -250,6 +297,7 @@
     public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        mBgColor = context.getColor(R.color.notification_shade_background_color);
         int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
         mExpandHelper = new ExpandHelper(getContext(), this,
@@ -261,18 +309,20 @@
         mSwipeHelper.setLongPressListener(mLongPressListener);
         mStackScrollAlgorithm = new StackScrollAlgorithm(context);
         initView(context);
+        setWillNotDraw(false);
         if (DEBUG) {
-            setWillNotDraw(false);
             mDebugPaint = new Paint();
             mDebugPaint.setColor(0xffff0000);
             mDebugPaint.setStrokeWidth(2);
             mDebugPaint.setStyle(Paint.Style.STROKE);
         }
         mFalsingManager = FalsingManager.getInstance(context);
+        mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
+        canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint);
         if (DEBUG) {
             int y = mTopPadding;
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -288,6 +338,20 @@
         }
     }
 
+    private void updateBackgroundDimming() {
+        float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
+        // We need to manually blend in the background color
+        int scrimColor = mScrimController.getScrimBehindColor();
+        // SRC_OVER blending Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc
+        float alphaInv = 1 - alpha;
+        int color = Color.argb((int) (alpha * 255 + alphaInv * Color.alpha(scrimColor)),
+                (int) (Color.red(mBgColor) + alphaInv * Color.red(scrimColor)),
+                (int) (Color.green(mBgColor) + alphaInv * Color.green(scrimColor)),
+                (int) (Color.blue(mBgColor) + alphaInv * Color.blue(scrimColor)));
+        mBackgroundPaint.setColor(color);
+        invalidate();
+    }
+
     private void initView(Context context) {
         mScroller = new OverScroller(getContext());
         setFocusable(true);
@@ -298,36 +362,23 @@
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
         mOverflingDistance = configuration.getScaledOverflingDistance();
-
-        mSidePaddings = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_side_padding);
         mCollapsedSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_min_height);
         mBottomStackPeekSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
         mStackScrollAlgorithm.initView(context);
-        mPaddingBetweenElementsDimmed = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
-        mPaddingBetweenElementsNormal = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_padding);
-        updatePadding(mAmbientState.isDimmed());
+        mPaddingBetweenElements = Math.max(1, context.getResources()
+                .getDimensionPixelSize(R.dimen.notification_divider_height));
+        mIncreasedPaddingBetweenElements = context.getResources()
+                .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
+
+        mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
         mMinTopOverScrollToEscape = getResources().getDimensionPixelSize(
                 R.dimen.min_top_overscroll_to_qs);
-        mNotificationTopPadding = getResources().getDimensionPixelSize(
-                R.dimen.notifications_top_padding);
         mCollapseSecondCardPadding = getResources().getDimensionPixelSize(
                 R.dimen.notification_collapse_second_card_padding);
     }
 
-    private void updatePadding(boolean dimmed) {
-        mPaddingBetweenElements = dimmed && mStackScrollAlgorithm.shouldScaleDimmed()
-                ? mPaddingBetweenElementsDimmed
-                : mPaddingBetweenElementsNormal;
-        mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
-        updateContentHeight();
-        notifyHeightChangeListener(null);
-    }
-
     private void notifyHeightChangeListener(ExpandableView view) {
         if (mOnHeightChangedListener != null) {
             mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
@@ -337,10 +388,7 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        int mode = MeasureSpec.getMode(widthMeasureSpec);
-        int size = MeasureSpec.getSize(widthMeasureSpec);
-        int childMeasureSpec = MeasureSpec.makeMeasureSpec(size - 2 * mSidePaddings, mode);
-        measureChildren(childMeasureSpec, heightMeasureSpec);
+        measureChildren(widthMeasureSpec, heightMeasureSpec);
     }
 
     @Override
@@ -368,6 +416,7 @@
             mRequestViewResizeAnimationOnLayout = false;
         }
         requestChildrenUpdate();
+        updateFirstAndLastBackgroundViews();
     }
 
     private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
@@ -378,18 +427,6 @@
     }
 
     public void updateSpeedBumpIndex(int newIndex) {
-        int currentIndex = indexOfChild(mSpeedBumpView);
-
-        // If we are currently layouted before the new speed bump index, we have to decrease it.
-        boolean validIndex = newIndex > 0;
-        if (newIndex > getChildCount() - 1) {
-            validIndex = false;
-            newIndex = -1;
-        }
-        if (validIndex && currentIndex != newIndex) {
-            changeViewPosition(mSpeedBumpView, newIndex);
-        }
-        updateSpeedBump(validIndex);
         mAmbientState.setSpeedBumpIndex(newIndex);
     }
 
@@ -686,8 +723,7 @@
         for (int childIdx = 0; childIdx < count; childIdx++) {
             ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
             if (slidingChild.getVisibility() == GONE
-                    || slidingChild instanceof StackScrollerDecorView
-                    || slidingChild == mSpeedBumpView) {
+                    || slidingChild instanceof StackScrollerDecorView) {
                 continue;
             }
             float childTop = slidingChild.getTranslationY();
@@ -714,8 +750,7 @@
         for (int childIdx = 0; childIdx < count; childIdx++) {
             ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
             if (slidingChild.getVisibility() == GONE
-                    || slidingChild instanceof StackScrollerDecorView
-                    || slidingChild == mSpeedBumpView) {
+                    || slidingChild instanceof StackScrollerDecorView) {
                 continue;
             }
             float childTop = slidingChild.getTranslationY();
@@ -1412,22 +1447,251 @@
 
     private void updateContentHeight() {
         int height = 0;
+        boolean previousNeedsIncreasedPaddings = false;
         for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE) {
+            ExpandableView expandableView = (ExpandableView) getChildAt(i);
+            if (expandableView.getVisibility() != View.GONE) {
+                boolean needsIncreasedPaddings = expandableView.needsIncreasedPadding();
                 if (height != 0) {
-                    // add the padding before this element
-                    height += mPaddingBetweenElements;
+                    int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
+                            ? mIncreasedPaddingBetweenElements
+                            : mPaddingBetweenElements;
+                    height += padding;
                 }
-                if (child instanceof ExpandableView) {
-                    ExpandableView expandableView = (ExpandableView) child;
-                    height += expandableView.getIntrinsicHeight();
-                }
+                previousNeedsIncreasedPaddings = needsIncreasedPaddings;
+                height += expandableView.getIntrinsicHeight();
             }
         }
         mContentHeight = height + mTopPadding;
     }
 
+    private void updateBackground() {
+        if (mAmbientState.isDark()) {
+            return;
+        }
+        updateBackgroundBounds();
+        if (!mCurrentBounds.equals(mBackgroundBounds)) {
+            if (mAnimateNextBackgroundTop || mAnimateNextBackgroundBottom || areBoundsAnimating()) {
+                startBackgroundAnimation();
+            } else {
+                mCurrentBounds.set(mBackgroundBounds);
+                applyCurrentBackgroundBounds();
+            }
+        } else {
+            if (mBottomAnimator != null) {
+                mBottomAnimator.cancel();
+            }
+            if (mTopAnimator != null) {
+                mTopAnimator.cancel();
+            }
+        }
+        mAnimateNextBackgroundBottom = false;
+        mAnimateNextBackgroundTop = false;
+    }
+
+    private boolean areBoundsAnimating() {
+        return mBottomAnimator != null || mTopAnimator != null;
+    }
+
+    private void startBackgroundAnimation() {
+        startBottomAnimation();
+        startTopAnimation();
+    }
+
+    private void startTopAnimation() {
+        int previousEndValue = mEndAnimationRect.top;
+        int newEndValue = mBackgroundBounds.top;
+        ObjectAnimator previousAnimator = mTopAnimator;
+        if (previousAnimator != null && previousEndValue == newEndValue) {
+            return;
+        }
+        if (!mAnimateNextBackgroundTop) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                int previousStartValue = mStartAnimationRect.top;
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                values[0].setIntValues(previousStartValue, newEndValue);
+                mStartAnimationRect.top = previousStartValue;
+                mEndAnimationRect.top = newEndValue;
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                setBackgroundTop(newEndValue);
+                return;
+            }
+        }
+        if (previousAnimator != null) {
+            previousAnimator.cancel();
+        }
+        ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
+                mCurrentBounds.top, newEndValue);
+        Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
+        animator.setInterpolator(interpolator);
+        animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mStartAnimationRect.top = -1;
+                mEndAnimationRect.top = -1;
+                mTopAnimator = null;
+            }
+        });
+        animator.start();
+        mStartAnimationRect.top = mCurrentBounds.top;
+        mEndAnimationRect.top = newEndValue;
+        mTopAnimator = animator;
+    }
+
+    private void startBottomAnimation() {
+        int previousStartValue = mStartAnimationRect.bottom;
+        int previousEndValue = mEndAnimationRect.bottom;
+        int newEndValue = mBackgroundBounds.bottom;
+        ObjectAnimator previousAnimator = mBottomAnimator;
+        if (previousAnimator != null && previousEndValue == newEndValue) {
+            return;
+        }
+        if (!mAnimateNextBackgroundBottom) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                values[0].setIntValues(previousStartValue, newEndValue);
+                mStartAnimationRect.bottom = previousStartValue;
+                mEndAnimationRect.bottom = newEndValue;
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                setBackgroundBottom(newEndValue);
+                return;
+            }
+        }
+        if (previousAnimator != null) {
+            previousAnimator.cancel();
+        }
+        ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
+                mCurrentBounds.bottom, newEndValue);
+        Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
+        animator.setInterpolator(interpolator);
+        animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mStartAnimationRect.bottom = -1;
+                mEndAnimationRect.bottom = -1;
+                mBottomAnimator = null;
+            }
+        });
+        animator.start();
+        mStartAnimationRect.bottom = mCurrentBounds.bottom;
+        mEndAnimationRect.bottom = newEndValue;
+        mBottomAnimator = animator;
+    }
+
+    private void setBackgroundTop(int top) {
+        mCurrentBounds.top = top;
+        applyCurrentBackgroundBounds();
+    }
+
+    public void setBackgroundBottom(int bottom) {
+        mCurrentBounds.bottom = bottom;
+        applyCurrentBackgroundBounds();
+    }
+
+    private void applyCurrentBackgroundBounds() {
+        mScrimController.setExcludedBackgroundArea(mCurrentBounds);
+        invalidate();
+    }
+
+    /**
+     * Update the background bounds to the new desired bounds
+     */
+    private void updateBackgroundBounds() {
+        mBackgroundBounds.left = (int) getX();
+        mBackgroundBounds.right = (int) (getX() + getWidth());
+        if (!mIsExpanded) {
+            mBackgroundBounds.top = 0;
+            mBackgroundBounds.bottom = 0;
+        }
+        ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
+        int top = 0;
+        if (firstView != null) {
+            int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(firstView);
+            if (mAnimateNextBackgroundTop
+                    || mTopAnimator == null && mCurrentBounds.top == finalTranslationY
+                    || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
+                // we're ending up at the same location as we are now, lets just skip the animation
+                top = finalTranslationY;
+            } else {
+                top = (int) firstView.getTranslationY();
+            }
+        }
+        ActivatableNotificationView lastView = mLastVisibleBackgroundChild;
+        int bottom = 0;
+        if (lastView != null) {
+            int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(lastView);
+            int finalHeight = StackStateAnimator.getFinalActualHeight(lastView);
+            int finalBottom = finalTranslationY + finalHeight;
+            finalBottom = Math.min(finalBottom, getHeight());
+            if (mAnimateNextBackgroundBottom
+                    || mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
+                    || mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
+                // we're ending up at the same location as we are now, lets just skip the animation
+                bottom = finalBottom;
+            } else {
+                bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight());
+                bottom = Math.min(bottom, getHeight());
+            }
+        }
+        mBackgroundBounds.top = top;
+        mBackgroundBounds.bottom = bottom;
+    }
+
+    private ActivatableNotificationView getFirstPinnedHeadsUp() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE
+                    && child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                if (row.isPinned()) {
+                    return row;
+                }
+            }
+        }
+        return null;
+    }
+
+    private ActivatableNotificationView getLastChildWithBackground() {
+        int childCount = getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE
+                    && child instanceof ActivatableNotificationView) {
+                return (ActivatableNotificationView) child;
+            }
+        }
+        return null;
+    }
+
+    private ActivatableNotificationView getFirstChildWithBackground() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != View.GONE
+                    && child instanceof ActivatableNotificationView) {
+                return (ActivatableNotificationView) child;
+            }
+        }
+        return null;
+    }
+
     /**
      * Fling the scroll view
      *
@@ -1489,7 +1753,7 @@
      */
     public void updateTopPadding(float qsHeight, int scrollY, boolean animate,
             boolean ignoreIntrinsicPadding) {
-        float start = qsHeight - scrollY + mNotificationTopPadding;
+        float start = qsHeight - scrollY;
         float stackHeight = getHeight() - start;
         int minStackHeight = getMinStackHeight();
         if (stackHeight <= minStackHeight) {
@@ -1505,10 +1769,6 @@
         setStackHeight(mLastSetStackHeight);
     }
 
-    public int getNotificationTopPadding() {
-        return mNotificationTopPadding;
-    }
-
     public int getMinStackHeight() {
         final ExpandableView firstChild = getFirstChildNotGone();
         final int firstChildMinHeight = firstChild != null ? firstChild.getMinHeight()
@@ -1655,7 +1915,7 @@
         }
         ((ExpandableView) child).setOnHeightChangedListener(null);
         mCurrentStackScrollState.removeViewStateForView(child);
-        updateScrollStateForRemovedChild(child);
+        updateScrollStateForRemovedChild((ExpandableView) child);
         boolean animationGenerated = generateRemoveAnimation(child);
         if (animationGenerated && !mSwipedOutViews.contains(child)) {
             // Add this view to an overlay in order to ensure that it will still be temporary
@@ -1754,9 +2014,12 @@
      *
      * @param removedChild the removed child
      */
-    private void updateScrollStateForRemovedChild(View removedChild) {
+    private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
         int startingPosition = getPositionInLinearLayout(removedChild);
-        int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
+        int padding = removedChild.needsIncreasedPadding()
+                ? mIncreasedPaddingBetweenElements :
+                mPaddingBetweenElements;
+        int childHeight = getIntrinsicHeight(removedChild) + padding;
         int endPosition = startingPosition + childHeight;
         if (endPosition <= mOwnScrollY) {
             // This child is fully scrolled of the top, so we have to deduct its height from the
@@ -1779,16 +2042,25 @@
 
     private int getPositionInLinearLayout(View requestedChild) {
         int position = 0;
+        boolean previousNeedsIncreasedPaddings = false;
         for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            boolean notGone = child.getVisibility() != View.GONE;
+            if (notGone) {
+                boolean needsIncreasedPaddings = child.needsIncreasedPadding();
+                if (position != 0) {
+                    int padding = needsIncreasedPaddings || previousNeedsIncreasedPaddings
+                            ? mIncreasedPaddingBetweenElements :
+                            mPaddingBetweenElements;
+                    position += padding;
+                }
+                previousNeedsIncreasedPaddings = needsIncreasedPaddings;
+            }
             if (child == requestedChild) {
                 return position;
             }
-            if (child.getVisibility() != View.GONE) {
+            if (notGone) {
                 position += getIntrinsicHeight(child);
-                if (i < getChildCount()-1) {
-                    position += mPaddingBetweenElements;
-                }
             }
         }
         return 0;
@@ -1800,6 +2072,20 @@
         onViewAddedInternal(child);
     }
 
+    private void updateFirstAndLastBackgroundViews() {
+        ActivatableNotificationView firstChild = getFirstChildWithBackground();
+        ActivatableNotificationView lastChild = getLastChildWithBackground();
+        if (mAnimationsEnabled && mIsExpanded) {
+            mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
+            mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
+        } else {
+            mAnimateNextBackgroundTop = false;
+            mAnimateNextBackgroundBottom = false;
+        }
+        mFirstVisibleBackgroundChild = firstChild;
+        mLastVisibleBackgroundChild = lastChild;
+    }
+
     private void onViewAddedInternal(View child) {
         updateHideSensitiveForChild(child);
         mStackScrollAlgorithm.notifyChildrenChanged(this);
@@ -1910,7 +2196,9 @@
         if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
             mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
                     mGoToFullShadeDelay);
+            setAnimationRunning(true);
             mAnimationEvents.clear();
+            updateBackground();
         } else {
             applyCurrentState();
         }
@@ -2396,6 +2684,7 @@
     }
 
     public void onChildAnimationFinished() {
+        setAnimationRunning(false);
         requestChildrenUpdate();
         runAnimationFinishedRunnables();
         clearViewOverlays();
@@ -2418,16 +2707,38 @@
      * See {@link AmbientState#setDimmed}.
      */
     public void setDimmed(boolean dimmed, boolean animate) {
-        mStackScrollAlgorithm.setDimmed(dimmed);
         mAmbientState.setDimmed(dimmed);
-        updatePadding(dimmed);
         if (animate && mAnimationsEnabled) {
             mDimmedNeedsAnimation = true;
             mNeedsAnimation =  true;
+            animateDimmed(dimmed);
+        } else {
+            setDimAmount(dimmed ? 1.0f : 0.0f);
         }
         requestChildrenUpdate();
     }
 
+    private void setDimAmount(float dimAmount) {
+        mDimAmount = dimAmount;
+        updateBackgroundDimming();
+    }
+
+    private void animateDimmed(boolean dimmed) {
+        if (mDimAnimator != null) {
+            mDimAnimator.cancel();
+        }
+        float target = dimmed ? 1.0f : 0.0f;
+        if (target == mDimAmount) {
+            return;
+        }
+        mDimAnimator = TimeAnimator.ofFloat(mDimAmount, target);
+        mDimAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED);
+        mDimAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mDimAnimator.addListener(mDimEndListener);
+        mDimAnimator.addUpdateListener(mDimUpdateListener);
+        mDimAnimator.start();
+    }
+
     public void setHideSensitive(boolean hideSensitive, boolean animate) {
         if (hideSensitive != mAmbientState.isHideSensitive()) {
             int childCount = getChildCount();
@@ -2466,30 +2777,10 @@
             mListener.onChildLocationsChanged(this);
         }
         runAnimationFinishedRunnables();
-    }
-
-    public void setSpeedBumpView(SpeedBumpView speedBumpView) {
-        mSpeedBumpView = speedBumpView;
-        addView(speedBumpView);
-    }
-
-    private void updateSpeedBump(boolean visible) {
-        boolean notGoneBefore = mSpeedBumpView.getVisibility() != GONE;
-        if (visible != notGoneBefore) {
-            int newVisibility = visible ? VISIBLE : GONE;
-            mSpeedBumpView.setVisibility(newVisibility);
-            if (visible) {
-                // Make invisible to ensure that the appear animation is played.
-                mSpeedBumpView.setInvisible();
-            } else {
-                // TODO: This doesn't really work, because the view is already set to GONE above.
-                generateRemoveAnimation(mSpeedBumpView);
-            }
-        }
+        updateBackground();
     }
 
     public void goToFullShade(long delay) {
-        updateSpeedBump(true /* visibility */);
         mDismissView.setInvisible();
         mEmptyShadeView.setInvisible();
         mGoToFullShadeNeedsAnimation = true;
@@ -2533,6 +2824,14 @@
             mNeedsAnimation =  true;
         }
         requestChildrenUpdate();
+        if (dark) {
+            setWillNotDraw(!DEBUG);
+            mScrimController.setExcludedBackgroundArea(null);
+        } else {
+            updateBackground();
+            setWillNotDraw(false);
+            // TODO: fade in background
+        }
     }
 
     private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
@@ -2731,7 +3030,7 @@
     }
 
     public int getDismissViewHeight() {
-        int height = mDismissView.getHeight() + mPaddingBetweenElementsNormal;
+        int height = mDismissView.getHeight() + mPaddingBetweenElements;
 
         // Hack: Accommodate for additional distance when we only have one notification and the
         // dismiss all button.
@@ -2881,6 +3180,12 @@
 
     public void setScrimController(ScrimController scrimController) {
         mScrimController = scrimController;
+        mScrimController.setScrimBehindChangeRunnable(new Runnable() {
+            @Override
+            public void run() {
+                updateBackgroundDimming();
+            }
+        });
     }
 
     public void forceNoOverlappingRendering(boolean force) {
@@ -2892,6 +3197,17 @@
         return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
     }
 
+    public void setAnimationRunning(boolean animationRunning) {
+        if (animationRunning != mAnimationRunning) {
+            if (animationRunning) {
+                getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater);
+            } else {
+                getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater);
+            }
+            mAnimationRunning = animationRunning;
+        }
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
@@ -2937,7 +3253,7 @@
 
                 // ANIMATION_TYPE_ADD
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -2946,7 +3262,7 @@
 
                 // ANIMATION_TYPE_REMOVE
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -2955,7 +3271,7 @@
 
                 // ANIMATION_TYPE_REMOVE_SWIPED_OUT
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -2964,37 +3280,35 @@
 
                 // ANIMATION_TYPE_TOP_PADDING_CHANGED
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
                         .animateDimmed()
-                        .animateScale()
                         .animateZ(),
 
                 // ANIMATION_TYPE_START_DRAG
                 new AnimationFilter()
-                        .animateAlpha(),
+                        .animateShadowAlpha(),
 
                 // ANIMATION_TYPE_SNAP_BACK
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight(),
 
                 // ANIMATION_TYPE_ACTIVATED_CHILD
                 new AnimationFilter()
-                        .animateScale()
-                        .animateAlpha(),
+                        .animateZ(),
 
                 // ANIMATION_TYPE_DIMMED
                 new AnimationFilter()
                         .animateY()
-                        .animateScale()
                         .animateDimmed(),
 
                 // ANIMATION_TYPE_CHANGE_POSITION
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateAlpha() // maybe the children change positions
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -3007,12 +3321,11 @@
 
                 // ANIMATION_TYPE_GO_TO_FULL_SHADE
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
                         .animateDimmed()
-                        .animateScale()
                         .animateZ()
                         .hasDelays(),
 
@@ -3022,7 +3335,7 @@
 
                 // ANIMATION_TYPE_VIEW_RESIZE
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -3031,6 +3344,7 @@
                 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
                 new AnimationFilter()
                         .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -3038,7 +3352,7 @@
 
                 // ANIMATION_TYPE_HEADS_UP_APPEAR
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -3046,7 +3360,7 @@
 
                 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -3054,7 +3368,7 @@
 
                 // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -3063,7 +3377,7 @@
 
                 // ANIMATION_TYPE_HEADS_UP_OTHER
                 new AnimationFilter()
-                        .animateAlpha()
+                        .animateShadowAlpha()
                         .animateHeight()
                         .animateTopInset()
                         .animateY()
@@ -3072,8 +3386,8 @@
                 // ANIMATION_TYPE_EVERYTHING
                 new AnimationFilter()
                         .animateAlpha()
+                        .animateShadowAlpha()
                         .animateDark()
-                        .animateScale()
                         .animateDimmed()
                         .animateHideSensitive()
                         .animateHeight()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 822012d..f6959f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.stack;
 
 import android.content.Context;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -27,6 +26,7 @@
 import com.android.systemui.statusbar.ExpandableView;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -41,15 +41,13 @@
     private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
     private static final int MAX_ITEMS_IN_TOP_STACK = 3;
 
-    public static final float DIMMED_SCALE = 0.98f;
-
     private int mPaddingBetweenElements;
+    private int mIncreasedPaddingBetweenElements;
     private int mCollapsedSize;
     private int mTopStackPeekSize;
     private int mBottomStackPeekSize;
     private int mZDistanceBetweenElements;
     private int mZBasicHeight;
-    private int mRoundedRectCornerRadius;
 
     private StackIndentationFunctor mTopStackIndentationFunctor;
     private StackIndentationFunctor mBottomStackIndentationFunctor;
@@ -61,16 +59,11 @@
     private ExpandableView mFirstChildWhileExpanding;
     private boolean mExpandedOnStart;
     private int mTopStackTotalSize;
-    private int mPaddingBetweenElementsDimmed;
-    private int mPaddingBetweenElementsNormal;
-    private int mNotificationsTopPadding;
     private int mBottomStackSlowDownLength;
     private int mTopStackSlowDownLength;
     private int mCollapseSecondCardPadding;
-    private boolean mScaleDimmed;
     private ExpandableView mFirstChild;
     private int mFirstChildMinHeight;
-    private boolean mDimmed;
 
     public StackScrollAlgorithm(Context context) {
         initView(context);
@@ -82,9 +75,6 @@
     }
 
     private void updatePadding() {
-        mPaddingBetweenElements = mDimmed && mScaleDimmed
-                ? mPaddingBetweenElementsDimmed
-                : mPaddingBetweenElementsNormal;
         mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements
                 + mTopStackPeekSize;
         mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
@@ -104,35 +94,25 @@
     }
 
     private void initConstants(Context context) {
-        mPaddingBetweenElementsDimmed = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
-        mPaddingBetweenElementsNormal = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_padding);
-        mNotificationsTopPadding = context.getResources()
-                .getDimensionPixelSize(R.dimen.notifications_top_padding);
+        mPaddingBetweenElements = Math.max(1, context.getResources()
+                .getDimensionPixelSize(R.dimen.notification_divider_height));
+        mIncreasedPaddingBetweenElements = context.getResources()
+                .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
         mCollapsedSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_min_height);
         mTopStackPeekSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.top_stack_peek_amount);
         mBottomStackPeekSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
-        mZDistanceBetweenElements = context.getResources()
-                .getDimensionPixelSize(R.dimen.z_distance_between_notifications);
+        mZDistanceBetweenElements = Math.max(1, context.getResources()
+                .getDimensionPixelSize(R.dimen.z_distance_between_notifications));
         mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
         mBottomStackSlowDownLength = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
         mTopStackSlowDownLength = context.getResources()
                 .getDimensionPixelSize(R.dimen.top_stack_slow_down_length);
-        mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
-                R.dimen.notification_material_rounded_rect_radius);
         mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize(
                 R.dimen.notification_collapse_second_card_padding);
-        mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi
-                >= DisplayMetrics.DENSITY_420;
-    }
-
-    public boolean shouldScaleDimmed() {
-        return mScaleDimmed;
     }
 
     public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -159,7 +139,7 @@
         scrollY = Math.max(0, scrollY);
         algorithmState.scrollY = (int) (scrollY + mFirstChildMinHeight + bottomOverScroll);
 
-        updateVisibleChildren(resultState, algorithmState);
+        initAlgorithmState(resultState, algorithmState);
 
         // Phase 1:
         findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState, ambientState);
@@ -212,8 +192,8 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState state = resultState.getViewStateForView(child);
-            float newYTranslation = state.yTranslation + state.height * (1f - state.scale) / 2f;
-            float newHeight = state.height * state.scale;
+            float newYTranslation = state.yTranslation;
+            float newHeight = state.height;
             // apply clipping and shadow
             float newNotificationEnd = newYTranslation + newHeight;
 
@@ -225,16 +205,6 @@
             } else {
                 clipHeight = newNotificationEnd - previousNotificationEnd;
                 clipHeight = Math.max(0.0f, clipHeight);
-                if (clipHeight != 0.0f) {
-
-                    // In the unlocked shade we have to clip a little bit higher because of the rounded
-                    // corners of the notifications, but only if we are not fully overlapped by
-                    // the top card.
-                    float clippingCorrection = state.dimmed
-                            ? 0
-                            : mRoundedRectCornerRadius * state.scale;
-                    clipHeight += clippingCorrection;
-                }
             }
 
             updateChildClippingAndBackground(state, newHeight, clipHeight,
@@ -252,7 +222,7 @@
                 } else {
                     previousNotificationIsSwiped = ambientState.getDraggedViews().contains(child);
                     previousNotificationEnd = newNotificationEnd;
-                    previousNotificationStart = newYTranslation + state.clipTopAmount * state.scale;
+                    previousNotificationStart = newYTranslation + state.clipTopAmount;
                 }
             }
         }
@@ -276,13 +246,13 @@
             float clipHeight, float backgroundHeight) {
         if (realHeight > clipHeight) {
             // Rather overlap than create a hole.
-            state.topOverLap = (int) Math.floor((realHeight - clipHeight) / state.scale);
+            state.topOverLap = (int) Math.floor(realHeight - clipHeight);
         } else {
             state.topOverLap = 0;
         }
         if (realHeight > backgroundHeight) {
             // Rather overlap than create a hole.
-            state.clipTopAmount = (int) Math.floor((realHeight - backgroundHeight) / state.scale);
+            state.clipTopAmount = (int) Math.floor(realHeight - backgroundHeight);
         } else {
             state.clipTopAmount = 0;
         }
@@ -305,9 +275,6 @@
             childViewState.dark = dark;
             childViewState.hideSensitive = hideSensitive;
             boolean isActivatedChild = activatedChild == child;
-            childViewState.scale = !mScaleDimmed || !dimmed || isActivatedChild
-                    ? 1.0f
-                    : DIMMED_SCALE;
             if (dimmed && isActivatedChild) {
                 childViewState.zTranslation += 2.0f * mZDistanceBetweenElements;
             }
@@ -331,7 +298,8 @@
                             nextChild);
                     // The child below the dragged one must be fully visible
                     if (ambientState.isShadeExpanded()) {
-                        viewState.alpha = 1;
+                        viewState.shadowAlpha = 1;
+                        viewState.hidden = false;
                     }
                 }
 
@@ -344,19 +312,28 @@
     }
 
     /**
-     * Update the visible children on the state.
+     * Initialize the algorithm state like updating the visible children.
      */
-    private void updateVisibleChildren(StackScrollState resultState,
+    private void initAlgorithmState(StackScrollState resultState,
             StackScrollAlgorithmState state) {
         ViewGroup hostView = resultState.getHostView();
         int childCount = hostView.getChildCount();
         state.visibleChildren.clear();
         state.visibleChildren.ensureCapacity(childCount);
+        state.increasedPaddingSet.clear();
         int notGoneIndex = 0;
+        ExpandableView lastView = null;
         for (int i = 0; i < childCount; i++) {
             ExpandableView v = (ExpandableView) hostView.getChildAt(i);
             if (v.getVisibility() != View.GONE) {
                 notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+                boolean needsIncreasedPadding = v.needsIncreasedPadding();
+                if (needsIncreasedPadding) {
+                    state.increasedPaddingSet.add(v);
+                    if (lastView != null) {
+                        state.increasedPaddingSet.add(lastView);
+                    }
+                }
                 if (v instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) v;
 
@@ -374,6 +351,7 @@
                         }
                     }
                 }
+                lastView = v;
             }
         }
     }
@@ -414,15 +392,17 @@
         int numberOfElementsCompletelyIn = algorithmState.partialInTop == 1.0f
                 ? algorithmState.lastTopStackIndex
                 : (int) algorithmState.itemsInTopStack;
+        int paddingAfterChild;
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState childViewState = resultState.getViewStateForView(child);
             childViewState.location = StackViewState.LOCATION_UNKNOWN;
+            paddingAfterChild = getPaddingAfterChild(algorithmState, child);
             int childHeight = getMaxAllowedChildHeight(child);
             int minHeight = child.getMinHeight();
             float yPositionInScrollViewAfterElement = yPositionInScrollView
                     + childHeight
-                    + mPaddingBetweenElements;
+                    + paddingAfterChild;
             float scrollOffset = yPositionInScrollView - algorithmState.scrollY +
                     mFirstChildMinHeight;
 
@@ -437,20 +417,20 @@
 
             // The y position after this element
             float nextYPosition = currentYPosition + childHeight +
-                    mPaddingBetweenElements;
+                    paddingAfterChild;
 
             if (i <= algorithmState.lastTopStackIndex) {
                 // Case 1:
                 // We are in the top Stack
-                updateStateForTopStackChild(algorithmState,
+                updateStateForTopStackChild(algorithmState, child,
                         numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset);
                 clampPositionToTopStackEnd(childViewState, childHeight);
 
                 // check if we are overlapping with the bottom stack
-                if (childViewState.yTranslation + childHeight + mPaddingBetweenElements
+                if (childViewState.yTranslation + childHeight + paddingAfterChild
                         >= bottomStackStart && !mIsExpansionChanging && i != 0) {
                     // we just collapse this element slightly
-                    int newSize = (int) Math.max(bottomStackStart - mPaddingBetweenElements -
+                    int newSize = (int) Math.max(bottomStackStart - paddingAfterChild -
                             childViewState.yTranslation, minHeight);
                     childViewState.height = newSize;
                     updateStateForChildTransitioningInBottom(algorithmState, bottomStackStart,
@@ -466,7 +446,7 @@
                     // According to the regular scroll view we are fully translated out of the
                     // bottom of the screen so we are fully in the bottom stack
                     updateStateForChildFullyInBottomStack(algorithmState,
-                            bottomStackStart, childViewState, minHeight, ambientState);
+                            bottomStackStart, childViewState, minHeight, ambientState, child);
                 } else {
                     // According to the regular scroll view we are currently translating out of /
                     // into the bottom of the screen
@@ -483,7 +463,8 @@
 
             // The first card is always rendered.
             if (i == 0) {
-                childViewState.alpha = 1.0f;
+                childViewState.hidden = false;
+                childViewState.shadowAlpha = 1.0f;
                 childViewState.yTranslation = Math.max(
                         mFirstChildMinHeight - algorithmState.scrollY, 0);
                 if (childViewState.yTranslation + childViewState.height
@@ -497,7 +478,7 @@
             if (childViewState.location == StackViewState.LOCATION_UNKNOWN) {
                 Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
             }
-            currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
+            currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
             yPositionInScrollView = yPositionInScrollViewAfterElement;
 
             childViewState.yTranslation += ambientState.getTopPadding()
@@ -506,6 +487,13 @@
         updateHeadsUpStates(resultState, algorithmState, ambientState);
     }
 
+    private int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
+            ExpandableView child) {
+        return algorithmState.increasedPaddingSet.contains(child)
+                ? mIncreasedPaddingBetweenElements
+                : mPaddingBetweenElements;
+    }
+
     private void updateHeadsUpStates(StackScrollState resultState,
             StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
@@ -530,8 +518,7 @@
                         bottomPosition);
             }
             if (row.isPinned()) {
-                childState.yTranslation = Math.max(childState.yTranslation,
-                        mNotificationsTopPadding);
+                childState.yTranslation = Math.max(childState.yTranslation, 0);
                 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
                 if (!isTopEntry) {
                     // Ensure that a headsUp doesn't vertically extend further than the heads-up at
@@ -610,7 +597,7 @@
         // This is the transitioning element on top of bottom stack, calculate how far we are in.
         algorithmState.partialInBottom = 1.0f - (
                 (transitioningPositionStart - currentYPosition) / (childHeight +
-                        mPaddingBetweenElements));
+                        getPaddingAfterChild(algorithmState, child)));
 
         // the offset starting at the transitionPosition of the bottom stack
         float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom);
@@ -618,12 +605,12 @@
         int newHeight = childHeight;
         if (childHeight > child.getMinHeight()) {
             newHeight = (int) Math.max(Math.min(transitioningPositionStart + offset -
-                    mPaddingBetweenElements - currentYPosition, childHeight),
+                    getPaddingAfterChild(algorithmState, child) - currentYPosition, childHeight),
                     child.getMinHeight());
             childViewState.height = newHeight;
         }
         childViewState.yTranslation = transitioningPositionStart + offset - newHeight
-                - mPaddingBetweenElements;
+                - getPaddingAfterChild(algorithmState, child);
 
         // We want at least to be at the end of the top stack when collapsing
         clampPositionToTopStackEnd(childViewState, newHeight);
@@ -632,22 +619,23 @@
 
     private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
             float transitioningPositionStart, StackViewState childViewState,
-            int minHeight, AmbientState ambientState) {
+            int minHeight, AmbientState ambientState, ExpandableView child) {
         float currentYPosition;
         algorithmState.itemsInBottomStack += 1.0f;
         if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
             // We are visually entering the bottom stack
             currentYPosition = transitioningPositionStart
                     + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack)
-                    - mPaddingBetweenElements;
+                    - getPaddingAfterChild(algorithmState, child);
             childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_PEEKING;
         } else {
             // we are fully inside the stack
             if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) {
-                childViewState.alpha = 0.0f;
+                childViewState.hidden = true;
+                childViewState.shadowAlpha = 0.0f;
             } else if (algorithmState.itemsInBottomStack
                     > MAX_ITEMS_IN_BOTTOM_STACK + 1) {
-                childViewState.alpha = 1.0f - algorithmState.partialInBottom;
+                childViewState.shadowAlpha = 1.0f - algorithmState.partialInBottom;
             }
             childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN;
             currentYPosition = ambientState.getInnerHeight();
@@ -658,7 +646,7 @@
     }
 
     private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
-            int numberOfElementsCompletelyIn, int i, int childHeight,
+            ExpandableView child, int numberOfElementsCompletelyIn, int i, int childHeight,
             StackViewState childViewState, float scrollOffset) {
 
 
@@ -669,10 +657,11 @@
         if (paddedIndex >= 0) {
 
             // We are currently visually entering the top stack
-            float distanceToStack = (childHeight + mPaddingBetweenElements)
+            float distanceToStack = (childHeight + getPaddingAfterChild(algorithmState, child))
                     - algorithmState.scrolledPixelsTop;
             if (i == algorithmState.lastTopStackIndex
-                    && distanceToStack > (mTopStackTotalSize + mPaddingBetweenElements)) {
+                    && distanceToStack > (mTopStackTotalSize
+                    + getPaddingAfterChild(algorithmState, child))) {
 
                 // Child is currently translating into stack but not yet inside slow down zone.
                 // Handle it like the regular scrollview.
@@ -682,7 +671,8 @@
                 float numItemsBefore;
                 if (i == algorithmState.lastTopStackIndex) {
                     numItemsBefore = 1.0f
-                            - (distanceToStack / (mTopStackTotalSize + mPaddingBetweenElements));
+                            - (distanceToStack / (mTopStackTotalSize
+                            + getPaddingAfterChild(algorithmState, child)));
                 } else {
                     numItemsBefore = algorithmState.itemsInTopStack - i;
                 }
@@ -694,10 +684,11 @@
             childViewState.location = StackViewState.LOCATION_TOP_STACK_PEEKING;
         } else {
             if (paddedIndex == -1) {
-                childViewState.alpha = 1.0f - algorithmState.partialInTop;
+                childViewState.shadowAlpha = 1.0f - algorithmState.partialInTop;
             } else {
                 // We are hidden behind the top card and faded out, so we can hide ourselves.
-                childViewState.alpha = 0.0f;
+                childViewState.hidden = true;
+                childViewState.shadowAlpha = 0.0f;
             }
             childViewState.yTranslation = mFirstChildMinHeight - childHeight;
             childViewState.location = StackViewState.LOCATION_TOP_STACK_HIDDEN;
@@ -718,15 +709,15 @@
         // The y Position if the element would be in a regular scrollView
         float yPositionInScrollView = 0.0f;
         int childCount = algorithmState.visibleChildren.size();
-
         // find the number of elements in the top stack.
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState childViewState = resultState.getViewStateForView(child);
             int childHeight = getMaxAllowedChildHeight(child);
+            int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
             float yPositionInScrollViewAfterElement = yPositionInScrollView
                     + childHeight
-                    + mPaddingBetweenElements;
+                    + paddingAfterChild;
             if (yPositionInScrollView < algorithmState.scrollY) {
                 if (i == 0 && algorithmState.scrollY <= mFirstChildMinHeight) {
 
@@ -754,7 +745,7 @@
                     algorithmState.scrolledPixelsTop = algorithmState.scrollY
                             - yPositionInScrollView;
                     algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight
-                            + mPaddingBetweenElements);
+                            + paddingAfterChild);
 
                     // Our element can be expanded, so this can get negative
                     algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
@@ -763,7 +754,7 @@
                     if (i == 0) {
                         // If it is expanded we have to collapse it to a new size
                         float newSize = yPositionInScrollViewAfterElement
-                                - mPaddingBetweenElements
+                                - paddingAfterChild
                                 - algorithmState.scrollY + mFirstChildMinHeight;
                         newSize = Math.max(mFirstChildMinHeight, newSize);
                         algorithmState.itemsInTopStack = 1.0f;
@@ -917,11 +908,6 @@
         }
     }
 
-    public void setDimmed(boolean dimmed) {
-        mDimmed = dimmed;
-        updatePadding();
-    }
-
     public void onReset(ExpandableView view) {
         if (view.equals(mFirstChildWhileExpanding)) {
             updateFirstChildMaxSizeToMaxHeight();
@@ -969,6 +955,11 @@
          * The children from the host view which are not gone.
          */
         public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
+
+        /**
+         * The children from the host that need an increased padding after them.
+         */
+        public final HashSet<ExpandableView> increasedPaddingSet = new HashSet<>();
     }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index e155d70..1fedc1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -25,7 +25,6 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.SpeedBumpView;
 
 import java.util.HashMap;
 import java.util.List;
@@ -83,8 +82,10 @@
         // initialize with the default values of the view
         viewState.height = view.getIntrinsicHeight();
         viewState.gone = view.getVisibility() == View.GONE;
-        viewState.alpha = 1;
+        viewState.alpha = 1f;
+        viewState.shadowAlpha = 1f;
         viewState.notGoneIndex = -1;
+        viewState.hidden = false;
     }
 
     public StackViewState getViewStateForView(View requestedView) {
@@ -107,9 +108,7 @@
             if (!applyState(child, state)) {
                 continue;
             }
-            if(child instanceof SpeedBumpView) {
-                performSpeedBumpAnimation(i, (SpeedBumpView) child, state, 0);
-            } else if (child instanceof DismissView) {
+            if (child instanceof DismissView) {
                 DismissView dismissView = (DismissView) child;
                 boolean visible = state.topOverLap < mClearAllTopPadding;
                 dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
@@ -146,6 +145,14 @@
             view.setActualHeight(newHeight, false /* notifyListeners */);
         }
 
+        float shadowAlpha = view.getShadowAlpha();
+        float newShadowAlpha = state.shadowAlpha;
+
+        // apply shadowAlpha
+        if (shadowAlpha != newShadowAlpha) {
+            view.setShadowAlpha(newShadowAlpha);
+        }
+
         // apply dimming
         view.setDimmed(state.dimmed, false /* animate */);
 
@@ -183,12 +190,10 @@
         float yTranslation = view.getTranslationY();
         float xTranslation = view.getTranslationX();
         float zTranslation = view.getTranslationZ();
-        float scale = view.getScaleX();
         float newAlpha = state.alpha;
         float newYTranslation = state.yTranslation;
         float newZTranslation = state.zTranslation;
-        float newScale = state.scale;
-        boolean becomesInvisible = newAlpha == 0.0f;
+        boolean becomesInvisible = newAlpha == 0.0f || state.hidden;
         if (alpha != newAlpha && xTranslation == 0) {
             // apply layer type
             boolean becomesFullyVisible = newAlpha == 1.0f;
@@ -225,34 +230,5 @@
         if (zTranslation != newZTranslation) {
             view.setTranslationZ(newZTranslation);
         }
-
-        // apply scale
-        if (scale != newScale) {
-            view.setScaleX(newScale);
-            view.setScaleY(newScale);
-        }
     }
-
-    public void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, StackViewState state,
-            long delay) {
-        View nextChild = getNextChildNotGone(i);
-        if (nextChild != null) {
-            float lineEnd = state.yTranslation + state.height / 2;
-            StackViewState nextState = getViewStateForView(nextChild);
-            boolean startIsAboveNext = nextState.yTranslation > lineEnd;
-            speedBump.animateDivider(startIsAboveNext, delay, null /* onFinishedRunnable */);
-        }
-    }
-
-    private View getNextChildNotGone(int childIndex) {
-        int childCount = mHostView.getChildCount();
-        for (int i = childIndex + 1; i < childCount; i++) {
-            View child = mHostView.getChildAt(i);
-            if (child.getVisibility() != View.GONE) {
-                return child;
-            }
-        }
-        return null;
-    }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 9d5e072..e75e8e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -22,13 +22,12 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.view.View;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.SpeedBumpView;
+import com.android.systemui.statusbar.Interpolators;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
@@ -55,24 +54,23 @@
 
     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
     private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
-    private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag;
     private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
     private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
     private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
+    private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag;
     private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
     private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
-    private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag;
     private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
     private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
     private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
+    private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag;
     private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
     private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
-    private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag;
     private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
     private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
     private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
+    private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag;
 
-    private final Interpolator mFastOutSlowInInterpolator;
     private final Interpolator mHeadsUpAppearInterpolator;
     private final int mGoToFullShadeAppearingTranslation;
     private final StackViewState mTmpState = new StackViewState();
@@ -99,8 +97,6 @@
 
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
-                android.R.interpolator.fast_out_slow_in);
         mGoToFullShadeAppearingTranslation =
                 hostLayout.getContext().getResources().getDimensionPixelSize(
                         R.dimen.go_to_full_shade_appearing_translation);
@@ -200,7 +196,6 @@
      */
     public void startStackAnimations(final ExpandableView child, StackViewState viewState,
             StackScrollState finalState, int i, long fixedDelay) {
-        final float alpha = viewState.alpha;
         boolean wasAdded = mNewAddChildren.contains(child);
         long duration = mCurrentLength;
         if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
@@ -212,14 +207,14 @@
         }
         boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
         boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
-        boolean scaleChanging = child.getScaleX() != viewState.scale;
-        boolean alphaChanging = alpha != child.getAlpha();
+        boolean alphaChanging = viewState.alpha != child.getAlpha();
         boolean heightChanging = viewState.height != child.getActualHeight();
+        boolean shadowAlphaChanging = viewState.shadowAlpha != child.getShadowAlpha();
         boolean darkChanging = viewState.dark != child.isDark();
         boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
         boolean hasDelays = mAnimationFilter.hasDelays;
-        boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
-                alphaChanging || heightChanging || topInsetChanging || darkChanging;
+        boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || alphaChanging
+                || heightChanging || topInsetChanging || darkChanging || shadowAlphaChanging;
         long delay = 0;
         if (fixedDelay != -1) {
             delay = fixedDelay;
@@ -234,6 +229,11 @@
             startHeightAnimation(child, viewState, duration, delay);
         }
 
+        // start shadow alpha animation
+        if (shadowAlphaChanging) {
+            startShadowAlphaAnimation(child, viewState, duration, delay);
+        }
+
         // start top inset animation
         if (topInsetChanging) {
             startInsetAnimation(child, viewState, duration, delay);
@@ -255,10 +255,7 @@
         if (wasAdded) {
             child.performAddAnimation(delay, mCurrentLength);
         }
-        if (child instanceof SpeedBumpView) {
-            finalState.performSpeedBumpAnimation(i, (SpeedBumpView) child, viewState,
-                    delay + duration);
-        } else if (child instanceof ExpandableNotificationRow) {
+        if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             row.startChildAnimation(finalState, this, delay, duration);
         }
@@ -275,13 +272,12 @@
     public void startViewAnimations(View child, ViewState viewState, long delay, long duration) {
         boolean wasVisible = child.getVisibility() == View.VISIBLE;
         final float alpha = viewState.alpha;
-        if (!wasVisible && alpha != 0 && !viewState.gone) {
+        if (!wasVisible && alpha != 0 && !viewState.gone && !viewState.hidden) {
             child.setVisibility(View.VISIBLE);
         }
         boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
         boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
-        boolean scaleChanging = child.getScaleX() != viewState.scale;
-        float childAlpha = child.getVisibility() == View.INVISIBLE ? 0.0f : child.getAlpha();
+        float childAlpha = child.getAlpha();
         boolean alphaChanging = viewState.alpha != childAlpha;
         if (child instanceof ExpandableView) {
             // We don't want views to change visibility when they are animating to GONE
@@ -298,11 +294,6 @@
             startZTranslationAnimation(child, viewState, duration, delay);
         }
 
-        // start scale animation
-        if (scaleChanging) {
-            startScaleAnimation(child, viewState, duration);
-        }
-
         // start alpha animation
         if (alphaChanging && child.getTranslationX() == 0) {
             startAlphaAnimation(child, viewState, duration, delay);
@@ -384,6 +375,64 @@
         return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
     }
 
+    private void startShadowAlphaAnimation(final ExpandableView child,
+            StackViewState viewState, long duration, long delay) {
+        Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA);
+        Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA);
+        float newEndValue = viewState.shadowAlpha;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA);
+        if (!mAnimationFilter.animateShadowAlpha) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_SHADOW_ALPHA, newStartValue);
+                child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setShadowAlpha(newEndValue);
+                return;
+            }
+        }
+
+        ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                child.setShadowAlpha((float) animation.getAnimatedValue());
+            }
+        });
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
+        animator.setDuration(newDuration);
+        if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+            animator.setStartDelay(delay);
+        }
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null);
+                child.setTag(TAG_START_SHADOW_ALPHA, null);
+                child.setTag(TAG_END_SHADOW_ALPHA, null);
+            }
+        });
+        startAnimator(animator);
+        child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator);
+        child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha());
+        child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
+    }
+
     private void startHeightAnimation(final ExpandableView child,
             StackViewState viewState, long duration, long delay) {
         Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
@@ -421,7 +470,7 @@
                         false /* notifyListeners */);
             }
         });
-        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
@@ -479,7 +528,7 @@
                 child.setClipTopAmount((int) animation.getAnimatedValue());
             }
         });
-        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
@@ -534,7 +583,7 @@
 
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
                 child.getAlpha(), newEndValue);
-        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         // Handle layer type
         child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
         animator.addListener(new AnimatorListenerAdapter() {
@@ -605,7 +654,7 @@
 
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
                 child.getTranslationZ(), newEndValue);
-        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
@@ -659,7 +708,7 @@
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
                 child.getTranslationY(), newEndValue);
         Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ?
-                mHeadsUpAppearInterpolator :mFastOutSlowInInterpolator;
+                mHeadsUpAppearInterpolator :Interpolators.FAST_OUT_SLOW_IN;
         animator.setInterpolator(interpolator);
         long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
@@ -683,60 +732,6 @@
         child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
     }
 
-    private void startScaleAnimation(final View child,
-            ViewState viewState, long duration) {
-        Float previousStartValue = getChildTag(child, TAG_START_SCALE);
-        Float previousEndValue = getChildTag(child, TAG_END_SCALE);
-        float newEndValue = viewState.scale;
-        if (previousEndValue != null && previousEndValue == newEndValue) {
-            return;
-        }
-        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE);
-        if (!mAnimationFilter.animateScale) {
-            // just a local update was performed
-            if (previousAnimator != null) {
-                // we need to increase all animation keyframes of the previous animator by the
-                // relative change to the end value
-                PropertyValuesHolder[] values = previousAnimator.getValues();
-                float relativeDiff = newEndValue - previousEndValue;
-                float newStartValue = previousStartValue + relativeDiff;
-                values[0].setFloatValues(newStartValue, newEndValue);
-                values[1].setFloatValues(newStartValue, newEndValue);
-                child.setTag(TAG_START_SCALE, newStartValue);
-                child.setTag(TAG_END_SCALE, newEndValue);
-                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
-                return;
-            } else {
-                // no new animation needed, let's just apply the value
-                child.setScaleX(newEndValue);
-                child.setScaleY(newEndValue);
-            }
-        }
-
-        PropertyValuesHolder holderX =
-                PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue);
-        PropertyValuesHolder holderY =
-                PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue);
-        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY);
-        animator.setInterpolator(mFastOutSlowInInterpolator);
-        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
-        animator.setDuration(newDuration);
-        animator.addListener(getGlobalAnimationFinishedListener());
-        // remove the tag when the animation is finished
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                child.setTag(TAG_ANIMATOR_SCALE, null);
-                child.setTag(TAG_START_SCALE, null);
-                child.setTag(TAG_END_SCALE, null);
-            }
-        });
-        startAnimator(animator);
-        child.setTag(TAG_ANIMATOR_SCALE, animator);
-        child.setTag(TAG_START_SCALE, child.getScaleX());
-        child.setTag(TAG_END_SCALE, newEndValue);
-    }
-
     private void startAnimator(ValueAnimator animator) {
         mAnimatorSet.add(animator);
         animator.start();
@@ -934,7 +929,7 @@
                         isRubberbanded);
             }
         });
-        overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
+        overScrollAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         overScrollAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -976,6 +971,22 @@
         }
     }
 
+    /**
+     * Get the end value of the height animation running on a view or the actualHeight
+     * if no animation is running.
+     */
+    public static float getFinalTranslationY(ExpandableView view) {
+        if (view == null) {
+            return 0;
+        }
+        ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
+        if (yAnimator == null) {
+            return view.getTranslationY();
+        } else {
+            return getChildTag(view, TAG_END_TRANSLATION_Y);
+        }
+    }
+
     public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
         mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
index 55ef440..41824ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
@@ -42,6 +42,7 @@
     public boolean dark;
     public boolean hideSensitive;
     public boolean belowSpeedBump;
+    public float shadowAlpha;
 
     /**
      * The amount which the view should be clipped from the top. This is calculated to
@@ -74,6 +75,7 @@
             StackViewState svs = (StackViewState) viewState;
             height = svs.height;
             dimmed = svs.dimmed;
+            shadowAlpha = svs.shadowAlpha;
             dark = svs.dark;
             hideSensitive = svs.hideSensitive;
             belowSpeedBump = svs.belowSpeedBump;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 3e538df..5beaac3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -29,21 +29,21 @@
     public float yTranslation;
     public float zTranslation;
     public boolean gone;
-    public float scale;
+    public boolean hidden;
 
     public void copyFrom(ViewState viewState) {
         alpha = viewState.alpha;
         yTranslation = viewState.yTranslation;
         zTranslation = viewState.zTranslation;
         gone = viewState.gone;
-        scale = viewState.scale;
+        hidden = viewState.hidden;
     }
 
     public void initFrom(View view) {
-        alpha = view.getVisibility() == View.INVISIBLE ? 0.0f : view.getAlpha();
+        alpha = view.getAlpha();
         yTranslation = view.getTranslationY();
         zTranslation = view.getTranslationZ();
         gone = view.getVisibility() == View.GONE;
-        scale = view.getScaleX();
+        hidden = false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index bed1e9e..e947ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -29,7 +29,6 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -38,7 +37,6 @@
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 
-import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 
 /**
@@ -96,6 +94,12 @@
             }
         }
     };
+    private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() {
+        @Override
+        public void run() {
+            movePipToFullscreen();
+        }
+    };
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -314,6 +318,12 @@
             // Post the message back to the UI thread.
             mHandler.post(mOnActivityPinnedRunnable);
         }
+
+        @Override
+        public void onPinnedActivityRestartAttempt() {
+            // Post the message back to the UI thread.
+            mHandler.post(mOnPinnedActivityRestartAttempt);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java b/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java
deleted file mode 100644
index 9438af1..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/IconPulser.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-public class IconPulser {
-    private static final float PULSE_SCALE = 1.1f;
-
-    private final Interpolator mFastOutSlowInInterpolator;
-
-    public IconPulser(Context context) {
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                android.R.interpolator.fast_out_slow_in);
-    }
-
-    public void start(final View target) {
-        if (target == null || target.getScaleX() != 1) return;  // n/a, or already running
-        target.animate().cancel();
-        target.animate().scaleX(PULSE_SCALE).scaleY(PULSE_SCALE)
-                .setInterpolator(mFastOutSlowInInterpolator)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        target.animate().scaleX(1).scaleY(1).setListener(null);
-                    }
-                });
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index ed6fc9e..b005a2b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -258,7 +258,6 @@
         if (w > max) {
             w = max;
         }
-        w -= mContext.getResources().getDimensionPixelSize(R.dimen.notification_side_padding) * 2;
         lp.width = w;
         mDialogView.setLayoutParams(lp);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
deleted file mode 100644
index f51e8ff..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
+++ /dev/null
@@ -1,431 +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.statusbar.phone;
-
-import org.mockito.InOrder;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.test.AndroidTestCase;
-
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.HashMap;
-import java.util.Map;
-
-/** Tests for the data model for the navigation bar app icons. */
-public class NavigationBarAppsModelTest extends AndroidTestCase {
-    private PackageManager mMockPackageManager;
-    private IPackageManager mMockIPackageManager;
-    private SharedPreferences mMockPrefs;
-    private SharedPreferences.Editor mMockEdit;
-    private UserManager mMockUserManager;
-
-    private NavigationBarAppsModel mModel;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        // Mockito setup boilerplate.
-        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
-        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-
-        final Context context = mock(Context.class);
-        mMockPackageManager = mock(PackageManager.class);
-        mMockIPackageManager = mock(IPackageManager.class);
-        mMockPrefs = mock(SharedPreferences.class);
-        mMockEdit = mock(SharedPreferences.Editor.class);
-        mMockUserManager = mock(UserManager.class);
-
-        when(context.getSharedPreferences(
-                "com.android.systemui.navbarapps", Context.MODE_PRIVATE)).thenReturn(mMockPrefs);
-        when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
-        when(context.getPackageManager()).thenReturn(mMockPackageManager);
-
-        setContext(context);
-
-        when(mMockUserManager.getUsers()).thenReturn(new ArrayList<UserInfo>());
-        // Assume the version pref is present and equal to the current version.
-        when(mMockPrefs.getInt("version", -1)).thenReturn(3);
-        when(mMockPrefs.edit()).thenReturn(mMockEdit);
-
-        when(mMockUserManager.getSerialNumberForUser(new UserHandle(2))).thenReturn(222L);
-        when(mMockUserManager.getSerialNumberForUser(new UserHandle(4))).thenReturn(444L);
-        when(mMockUserManager.getSerialNumberForUser(new UserHandle(5))).thenReturn(555L);
-        when(mMockUserManager.getUserForSerialNumber(222L)).thenReturn(new UserHandle(2));
-        when(mMockUserManager.getUserForSerialNumber(444L)).thenReturn(new UserHandle(4));
-        when(mMockUserManager.getUserForSerialNumber(555L)).thenReturn(new UserHandle(5));
-
-        UserInfo ui2 = new UserInfo();
-        ui2.profileGroupId = 999;
-        UserInfo ui4 = new UserInfo();
-        ui4.profileGroupId = 999;
-        UserInfo ui5 = new UserInfo();
-        ui5.profileGroupId = 999;
-        when(mMockUserManager.getUserInfo(2)).thenReturn(ui2);
-        when(mMockUserManager.getUserInfo(4)).thenReturn(ui4);
-        when(mMockUserManager.getUserInfo(5)).thenReturn(ui5);
-
-        mModel = new NavigationBarAppsModel(context) {
-            @Override
-            protected IPackageManager getPackageManager() {
-                return mMockIPackageManager;
-            }
-        };
-    }
-
-    /** Tests resolveApp(). */
-    public void testResolveApp() {
-        ActivityInfo mockNonExportedActivityInfo = new ActivityInfo();
-        mockNonExportedActivityInfo.exported = false;
-        ActivityInfo mockExportedActivityInfo = new ActivityInfo();
-        mockExportedActivityInfo.exported = true;
-        try {
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package1", "class1"), 0, 4)).
-                    thenReturn(mockNonExportedActivityInfo);
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package2", "class2"), 0, 5)).
-                    thenThrow(new RemoteException());
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package3", "class3"), 0, 6)).
-                    thenReturn(mockExportedActivityInfo);
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package4", "class4"), 0, 7)).
-                    thenReturn(mockExportedActivityInfo);
-        } catch (RemoteException e) {
-            fail("RemoteException can't happen in the test, but it happened.");
-        }
-
-        // Assume some installed activities.
-        ActivityInfo ai0 = new ActivityInfo();
-        ai0.packageName = "package0";
-        ai0.name = "class0";
-        ActivityInfo ai1 = new ActivityInfo();
-        ai1.packageName = "package4";
-        ai1.name = "class4";
-        ResolveInfo ri0 = new ResolveInfo();
-        ri0.activityInfo = ai0;
-        ResolveInfo ri1 = new ResolveInfo();
-        ri1.activityInfo = ai1;
-        when(mMockPackageManager
-                .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
-                .thenReturn(Arrays.asList(ri0, ri1));
-
-        mModel.setCurrentUser(3);
-        // Unlauncheable (for various reasons) apps.
-        assertEquals(null, mModel.resolveApp(
-                new AppInfo(new ComponentName("package0", "class0"), new UserHandle(3))));
-        mModel.setCurrentUser(4);
-        assertEquals(null, mModel.resolveApp(
-                new AppInfo(new ComponentName("package1", "class1"), new UserHandle(4))));
-        mModel.setCurrentUser(5);
-        assertEquals(null, mModel.resolveApp(
-                new AppInfo(new ComponentName("package2", "class2"), new UserHandle(5))));
-        mModel.setCurrentUser(6);
-        assertEquals(null, mModel.resolveApp(
-                new AppInfo(new ComponentName("package3", "class3"), new UserHandle(6))));
-
-        // A launcheable app.
-        mModel.setCurrentUser(7);
-        NavigationBarAppsModel.ResolvedApp resolvedApp = mModel.resolveApp(
-                new AppInfo(new ComponentName("package4", "class4"), new UserHandle(7)));
-        assertNotNull(resolvedApp);
-        Intent intent = resolvedApp.launchIntent;
-        assertEquals(new ComponentName("package4", "class4"), intent.getComponent());
-        assertEquals("package4", intent.getPackage());
-        assertEquals(ri1, resolvedApp.ri);
-    }
-
-    /** Initializes the model from SharedPreferences for a few app activites. */
-    private void initializeModelFromPrefs() {
-        // Assume several apps are stored.
-        when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
-        when(mMockPrefs.getString("222|app_0", null)).thenReturn("package1/class1");
-        when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(444L);
-        when(mMockPrefs.getString("222|app_1", null)).thenReturn("package2/class2");
-        when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(555L);
-
-        ActivityInfo mockActivityInfo = new ActivityInfo();
-        mockActivityInfo.exported = true;
-        try {
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package1", "class1"), 0, 4)).thenReturn(mockActivityInfo);
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package2", "class2"), 0, 5)).thenReturn(mockActivityInfo);
-        } catch (RemoteException e) {
-            fail("RemoteException can't happen in the test, but it happened.");
-        }
-
-        // Assume some installed activities.
-        ActivityInfo ai0 = new ActivityInfo();
-        ai0.packageName = "package0";
-        ai0.name = "class0";
-        ActivityInfo ai1 = new ActivityInfo();
-        ai1.packageName = "package1";
-        ai1.name = "class1";
-        ActivityInfo ai2 = new ActivityInfo();
-        ai2.packageName = "package2";
-        ai2.name = "class2";
-        ResolveInfo ri0 = new ResolveInfo();
-        ri0.activityInfo = ai0;
-        ResolveInfo ri1 = new ResolveInfo();
-        ri1.activityInfo = ai1;
-        ResolveInfo ri2 = new ResolveInfo();
-        ri2.activityInfo = ai2;
-        when(mMockPackageManager
-                .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
-                .thenReturn(Arrays.asList(ri0, ri1, ri2));
-
-        mModel.setCurrentUser(2);
-    }
-
-    /** Tests initializing the model from SharedPreferences. */
-    public void testInitializeFromPrefs() {
-        initializeModelFromPrefs();
-        List<AppInfo> apps = mModel.getApps();
-        assertEquals(2, apps.size());
-        assertEquals("package1/class1", apps.get(0).getComponentName().flattenToString());
-        assertEquals(new UserHandle(4), apps.get(0).getUser());
-        assertEquals("package2/class2", apps.get(1).getComponentName().flattenToString());
-        assertEquals(new UserHandle(5), apps.get(1).getUser());
-    }
-
-    /** Tests initializing the model when the SharedPreferences aren't available. */
-    public void testInitializeDefaultApps() {
-        // Assume the user's app count pref isn't available.
-        when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1);
-
-        // Assume some installed activities.
-        ActivityInfo ai1 = new ActivityInfo();
-        ai1.packageName = "package1";
-        ai1.name = "class1";
-        ActivityInfo ai2 = new ActivityInfo();
-        ai2.packageName = "package2";
-        ai2.name = "class2";
-        ResolveInfo ri1 = new ResolveInfo();
-        ri1.activityInfo = ai1;
-        ResolveInfo ri2 = new ResolveInfo();
-        ri2.activityInfo = ai2;
-        when(mMockPackageManager
-                .queryIntentActivitiesAsUser(any(Intent.class), eq(0), eq(2)))
-                .thenReturn(Arrays.asList(ri1, ri2));
-
-        // Setting the user should load the installed activities.
-        mModel.setCurrentUser(2);
-        List<AppInfo> apps = mModel.getApps();
-        assertEquals(2, apps.size());
-        assertEquals("package1/class1", apps.get(0).getComponentName().flattenToString());
-        assertEquals(new UserHandle(2), apps.get(0).getUser());
-        assertEquals("package2/class2", apps.get(1).getComponentName().flattenToString());
-        assertEquals(new UserHandle(2), apps.get(1).getUser());
-        InOrder order = inOrder(mMockEdit);
-        order.verify(mMockEdit).apply();
-        order.verify(mMockEdit).putInt("222|app_count", 2);
-        order.verify(mMockEdit).putString("222|app_0", "package1/class1");
-        order.verify(mMockEdit).putLong("222|app_user_0", 222L);
-        order.verify(mMockEdit).putString("222|app_1", "package2/class2");
-        order.verify(mMockEdit).putLong("222|app_user_1", 222L);
-        order.verify(mMockEdit).apply();
-        verifyNoMoreInteractions(mMockEdit);
-    }
-
-    /** Tests initializing the model if one of the prefs is missing. */
-    public void testInitializeWithMissingPref() {
-        // Assume two apps are nominally stored.
-        when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
-        when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0");
-        when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L);
-
-        // But assume one pref is missing.
-        when(mMockPrefs.getString("222|app_1", null)).thenReturn(null);
-
-        ActivityInfo mockActivityInfo = new ActivityInfo();
-        mockActivityInfo.exported = true;
-        try {
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
-        } catch (RemoteException e) {
-            fail("RemoteException can't happen in the test, but it happened.");
-        }
-
-        ActivityInfo ai0 = new ActivityInfo();
-        ai0.packageName = "package0";
-        ai0.name = "class0";
-        ResolveInfo ri0 = new ResolveInfo();
-        ri0.activityInfo = ai0;
-        when(mMockPackageManager
-                .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
-                .thenReturn(Arrays.asList(ri0));
-
-        // Initializing the model should load from prefs and skip the missing one.
-        mModel.setCurrentUser(2);
-        List<AppInfo> apps = mModel.getApps();
-        assertEquals(1, apps.size());
-        assertEquals("package0/class0", apps.get(0).getComponentName().flattenToString());
-        assertEquals(new UserHandle(5), apps.get(0).getUser());
-        InOrder order = inOrder(mMockEdit);
-        order.verify(mMockEdit).putInt("222|app_count", 1);
-        order.verify(mMockEdit).putString("222|app_0", "package0/class0");
-        order.verify(mMockEdit).putLong("222|app_user_0", 555L);
-        order.verify(mMockEdit).apply();
-        verifyNoMoreInteractions(mMockEdit);
-    }
-
-    /** Tests initializing the model if one of the apps is unlauncheable. */
-    public void testInitializeWithUnlauncheableApp() {
-        // Assume two apps are nominally stored.
-        when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
-        when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0");
-        when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L);
-        when(mMockPrefs.getString("222|app_1", null)).thenReturn("package1/class1");
-        when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(444L);
-
-        ActivityInfo mockActivityInfo = new ActivityInfo();
-        mockActivityInfo.exported = true;
-        try {
-            when(mMockIPackageManager.getActivityInfo(
-                    new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
-        } catch (RemoteException e) {
-            fail("RemoteException can't happen in the test, but it happened.");
-        }
-
-        ActivityInfo ai0 = new ActivityInfo();
-        ai0.packageName = "package0";
-        ai0.name = "class0";
-        ResolveInfo ri0 = new ResolveInfo();
-        ri0.activityInfo = ai0;
-        when(mMockPackageManager
-                .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
-                .thenReturn(Arrays.asList(ri0));
-
-        // Initializing the model should load from prefs and skip the unlauncheable one.
-        mModel.setCurrentUser(2);
-        List<AppInfo> apps = mModel.getApps();
-        assertEquals(1, apps.size());
-        assertEquals("package0/class0", apps.get(0).getComponentName().flattenToString());
-        assertEquals(new UserHandle(5), apps.get(0).getUser());
-
-        // Once an unlauncheable app is detected, the model should save all apps excluding the
-        // unlauncheable one.
-        verify(mMockEdit).putInt("222|app_count", 1);
-        verify(mMockEdit).putString("222|app_0", "package0/class0");
-        verify(mMockEdit).putLong("222|app_user_0", 555L);
-        verify(mMockEdit).apply();
-        verifyNoMoreInteractions(mMockEdit);
-    }
-
-    /** Tests saving the model to SharedPreferences. */
-    public void testSavePrefs() {
-        initializeModelFromPrefs();
-
-        mModel.setApps(mModel.getApps());
-        verify(mMockEdit).putInt("222|app_count", 2);
-        verify(mMockEdit).putString("222|app_0", "package1/class1");
-        verify(mMockEdit).putLong("222|app_user_0", 444L);
-        verify(mMockEdit).putString("222|app_1", "package2/class2");
-        verify(mMockEdit).putLong("222|app_user_1", 555L);
-        verify(mMockEdit).apply();
-        verifyNoMoreInteractions(mMockEdit);
-    }
-
-    /** Tests cleaning all prefs on a version change. */
-    public void testVersionChange() {
-        // Assume the version pref changed.
-        when(mMockPrefs.getInt("version", -1)).thenReturn(1);
-
-        new NavigationBarAppsModel(getContext());
-        verify(mMockEdit).clear();
-        verify(mMockEdit).putInt("version", 3);
-        verify(mMockEdit).apply();
-        verifyNoMoreInteractions(mMockEdit);
-    }
-
-    /** Tests cleaning prefs for deleted users. */
-    public void testCleaningDeletedUsers() {
-        // Users on the device.
-        final UserInfo user1 = new UserInfo(11, "", 0);
-        user1.serialNumber = 1111;
-        final UserInfo user2 = new UserInfo(13, "", 0);
-        user2.serialNumber = 1313;
-
-        when(mMockUserManager.getUsers()).thenReturn(Arrays.asList(user1, user2));
-
-        when(mMockPrefs.edit()).
-                thenReturn(mMockEdit).
-                thenReturn(mock(SharedPreferences.Editor.class));
-
-        // Assume the user's app count pref isn't available. This will trigger clearing deleted
-        // users' prefs.
-        when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1);
-
-        final Map allPrefs = new HashMap<String, Object>();
-        allPrefs.put("version", null);
-        allPrefs.put("some_strange_pref", null);
-        allPrefs.put("", null);
-        allPrefs.put("|", null);
-        allPrefs.put("1313|app_count", null);
-        allPrefs.put("1212|app_count", null);
-        when(mMockPrefs.getAll()).thenReturn(allPrefs);
-
-        // Setting the user should remove prefs for deleted users.
-        mModel.setCurrentUser(2);
-        verify(mMockEdit).remove("some_strange_pref");
-        verify(mMockEdit).remove("");
-        verify(mMockEdit).remove("|");
-        verify(mMockEdit).remove("1212|app_count");
-        verify(mMockEdit).apply();
-        verifyNoMoreInteractions(mMockEdit);
-    }
-
-    /** Tests the apps-changed listener. */
-    public void testAppsChangedListeners() {
-        NavigationBarAppsModel.OnAppsChangedListener listener =
-                mock(NavigationBarAppsModel.OnAppsChangedListener.class);
-
-        mModel.addOnAppsChangedListener(listener);
-        mModel.setApps(new ArrayList<AppInfo>());
-        verify(listener).onPinnedAppsChanged();
-        verifyNoMoreInteractions(listener);
-
-        mModel.removeOnAppsChangedListener(listener);
-        mModel.setApps(new ArrayList<AppInfo>());
-        verifyNoMoreInteractions(listener);
-    }
-}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 88e55dd..697777c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -339,5 +339,8 @@
 
     // Logged when the user calls an emergency contact.
     ACTION_CALL_EMERGENCY_CONTACT = 283;
+
+    // QS Tile for Data Saver.
+    QS_DATA_SAVER = 284;
   }
 }
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index a4876b9..a71ba636 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -16,14 +16,16 @@
 
 package android.renderscript;
 
+import java.nio.ByteBuffer;
 import java.util.HashMap;
+
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.view.Surface;
-import android.util.Log;
 import android.graphics.Canvas;
 import android.os.Trace;
+import android.util.Log;
+import android.view.Surface;
 
 /**
  * <p> This class provides the primary method through which data is passed to
@@ -78,6 +80,8 @@
     OnBufferAvailableListener mBufferNotifier;
 
     private Surface mGetSurfaceSurface = null;
+    private ByteBuffer mByteBuffer = null;
+    private long mByteBufferStride = -1;
 
     private Element.DataType validateObjectIsPrimitiveArray(Object d, boolean checkType) {
         final Class c = d.getClass();
@@ -2050,6 +2054,59 @@
     }
 
     /**
+     * @hide
+     * Gets or creates a ByteBuffer that contains the raw data of the current Allocation.
+     * If the Allocation is created with USAGE_IO_INPUT, the returned ByteBuffer
+     * would contain the up-to-date data as READ ONLY.
+     * For a 2D or 3D Allocation, the raw data maybe padded so that each row of
+     * the Allocation has certain alignment. The size of each row including padding,
+     * called stride, can be queried using the {@link #getStride()} method.
+     *
+     * Note: Operating on the ByteBuffer of a destroyed Allocation will triger errors.
+     *
+     * @return ByteBuffer The ByteBuffer associated with raw data pointer of the Allocation.
+     */
+    public ByteBuffer getByteBuffer() {
+        // Create a new ByteBuffer if it is not initialized or using IO_INPUT.
+        if (mType.hasFaces()) {
+            throw new RSInvalidStateException("Cubemap is not supported for getByteBuffer().");
+        }
+        if (mType.getYuv() == android.graphics.ImageFormat.NV21 ||
+            mType.getYuv() == android.graphics.ImageFormat.YV12 ||
+            mType.getYuv() == android.graphics.ImageFormat.YUV_420_888 ) {
+            throw new RSInvalidStateException("YUV format is not supported for getByteBuffer().");
+        }
+        if (mByteBuffer == null || (mUsage & USAGE_IO_INPUT) != 0) {
+            int xBytesSize = mType.getX() * mType.getElement().getBytesSize();
+            long[] stride = new long[1];
+            mByteBuffer = mRS.nAllocationGetByteBuffer(getID(mRS), stride, xBytesSize, mType.getY(), mType.getZ());
+            mByteBufferStride = stride[0];
+        }
+        if ((mUsage & USAGE_IO_INPUT) != 0) {
+            return mByteBuffer.asReadOnlyBuffer();
+        }
+        return mByteBuffer;
+    }
+
+    /**
+     * @hide
+     * Gets the stride of the Allocation.
+     * For a 2D or 3D Allocation, the raw data maybe padded so that each row of
+     * the Allocation has certain alignment. The size of each row including such
+     * padding is called stride.
+     *
+     * @return the stride. For 1D Allocation, the stride will be the number of
+     *         bytes of this Allocation. For 2D and 3D Allocations, the stride
+     *         will be the stride in X dimension measuring in bytes.
+     */
+    public long getStride() {
+        if (mByteBufferStride == -1) {
+            getByteBuffer();
+        }
+        return mByteBufferStride;
+    }
+
+    /**
      * Returns the handle to a raw buffer that is being managed by the screen
      * compositor. This operation is only valid for Allocations with {@link
      * #USAGE_IO_INPUT}.
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 3668ddd..4788223 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -18,17 +18,18 @@
 
 import java.io.File;
 import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
 import android.graphics.SurfaceTexture;
-import android.util.Log;
-import android.view.Surface;
 import android.os.SystemProperties;
 import android.os.Trace;
-import java.util.ArrayList;
+import android.util.Log;
+import android.view.Surface;
 
 // TODO: Clean up the whitespace that separates methods in this class.
 
@@ -489,6 +490,13 @@
         validate();
         rsnAllocationSyncAll(mContext, alloc, src);
     }
+
+    native ByteBuffer rsnAllocationGetByteBuffer(long con, long alloc, long[] stride, int xBytesSize, int dimY, int dimZ);
+    synchronized ByteBuffer nAllocationGetByteBuffer(long alloc, long[] stride, int xBytesSize, int dimY, int dimZ) {
+        validate();
+        return rsnAllocationGetByteBuffer(mContext, alloc, stride, xBytesSize, dimY, dimZ);
+    }
+
     native Surface rsnAllocationGetSurface(long con, long alloc);
     synchronized Surface nAllocationGetSurface(long alloc) {
         validate();
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index 84f980d..2b06780 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -315,7 +315,6 @@
     /**
      * Only intended for use by generated reflected code.  (General reduction)
      *
-     * @hide
      */
     protected void reduce(int slot, Allocation[] ains, Allocation aout, LaunchOptions sc) {
         mRS.validate();
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 3954070..398d89b 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -2752,7 +2752,43 @@
     return (jint)sizeof(void*);
 }
 
+static jobject
+nAllocationGetByteBuffer(JNIEnv *_env, jobject _this, jlong con, jlong alloc,
+                        jlongArray strideArr, jint xBytesSize,
+                        jint dimY, jint dimZ) {
+    if (kLogApi) {
+        ALOGD("nAllocationGetByteBuffer, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
+    }
 
+    jlong *jStridePtr = _env->GetLongArrayElements(strideArr, nullptr);
+    if (jStridePtr == nullptr) {
+        ALOGE("Failed to get Java array elements: strideArr");
+        return 0;
+    }
+
+    size_t strideIn = xBytesSize;
+    void* ptr = nullptr;
+    if (alloc != 0) {
+        ptr = rsAllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
+                                     RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0,
+                                     &strideIn, sizeof(size_t));
+    }
+
+    jobject byteBuffer = nullptr;
+    if (ptr != nullptr) {
+        size_t bufferSize = strideIn;
+        jStridePtr[0] = strideIn;
+        if (dimY > 0) {
+            bufferSize *= dimY;
+        }
+        if (dimZ > 0) {
+            bufferSize *= dimZ;
+        }
+        byteBuffer = _env->NewDirectByteBuffer(ptr, (jlong) bufferSize);
+    }
+    _env->ReleaseLongArrayElements(strideArr, jStridePtr, 0);
+    return byteBuffer;
+}
 // ---------------------------------------------------------------------------
 
 
@@ -2909,6 +2945,7 @@
 {"rsnMeshGetIndices",                "(JJ[J[II)V",                            (void*)nMeshGetIndices },
 
 {"rsnSystemGetPointerSize",          "()I",                                   (void*)nSystemGetPointerSize },
+{"rsnAllocationGetByteBuffer",       "(JJ[JIII)Ljava/nio/ByteBuffer;",        (void*)nAllocationGetByteBuffer },
 };
 
 static int registerFuncs(JNIEnv *_env)
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d63dd0c..46198b0 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1920,7 +1920,9 @@
                 synchronized (mBackupParticipants) {
                     if (replacing) {
                         // This is the package-replaced case; we just remove the entry
-                        // under the old uid and fall through to re-add.
+                        // under the old uid and fall through to re-add.  If an app
+                        // just added key/value backup participation, this picks it up
+                        // as a known participant.
                         removePackageParticipantsLocked(pkgList, uid);
                     }
                     addPackageParticipantsLocked(pkgList);
@@ -1933,6 +1935,14 @@
                         if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
                             enqueueFullBackup(packageName, now);
                             scheduleNextFullBackupJob(0);
+                        } else {
+                            // The app might have just transitioned out of full-data into
+                            // doing key/value backups, or might have just disabled backups
+                            // entirely.  Make sure it is no longer in the full-data queue.
+                            synchronized (mQueueLock) {
+                                dequeueFullBackupLocked(packageName);
+                            }
+                            writeFullBackupScheduleAsync();
                         }
 
                         // Transport maintenance: rebind to known existing transports that have
@@ -1964,6 +1974,9 @@
                 if (replacing) {
                     // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
                 } else {
+                    // Outright removal.  In the full-data case, the app will be dropped
+                    // from the queue when its (now obsolete) name comes up again for
+                    // backup.
                     synchronized (mBackupParticipants) {
                         removePackageParticipantsLocked(pkgList, uid);
                     }
@@ -4190,11 +4203,21 @@
                         // as well as any explicit mention of the 'special' shared-storage agent
                         // package (we handle that one at the end).
                         if (MORE_DEBUG) {
-                            Slog.d(TAG, "Ignoring not eligible package " + pkg);
+                            Slog.d(TAG, "Ignoring ineligible package " + pkg);
                         }
                         sendBackupOnResult(mBackupObserver, pkg,
                             BackupManager.ERROR_BACKUP_NOT_ALLOWED);
                         continue;
+                    } else if (!appGetsFullBackup(info)) {
+                        // Cull any packages that are found in the queue but now aren't supposed
+                        // to get full-data backup operations.
+                        if (MORE_DEBUG) {
+                            Slog.d(TAG, "Ignoring full-data backup of key/value participant "
+                                    + pkg);
+                        }
+                        sendBackupOnResult(mBackupObserver, pkg,
+                                BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+                        continue;
                     } else if (appIsStopped(info.applicationInfo)) {
                         // Cull any packages in the 'stopped' state: they've either just been
                         // installed or have explicitly been force-stopped by the user.  In both
@@ -4592,21 +4615,28 @@
     }
 
     /**
+     * Remove a package from the full-data queue.
+     */
+    void dequeueFullBackupLocked(String packageName) {
+        final int N = mFullBackupQueue.size();
+        for (int i = N-1; i >= 0; i--) {
+            final FullBackupEntry e = mFullBackupQueue.get(i);
+            if (packageName.equals(e.packageName)) {
+                mFullBackupQueue.remove(i);
+            }
+        }
+    }
+
+    /**
      * Enqueue full backup for the given app, with a note about when it last ran.
      */
     void enqueueFullBackup(String packageName, long lastBackedUp) {
         FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
         synchronized (mQueueLock) {
-            int N = mFullBackupQueue.size();
             // First, sanity check that we aren't adding a duplicate.  Slow but
             // straightforward; we'll have at most on the order of a few hundred
             // items in this list.
-            for (int i = N-1; i >= 0; i--) {
-                final FullBackupEntry e = mFullBackupQueue.get(i);
-                if (packageName.equals(e.packageName)) {
-                    mFullBackupQueue.remove(i);
-                }
-            }
+            dequeueFullBackupLocked(packageName);
 
             // This is also slow but easy for modest numbers of apps: work backwards
             // from the end of the queue until we find an item whose last backup
@@ -4700,21 +4730,24 @@
                 return false;
             }
 
-            if (mFullBackupQueue.size() == 0) {
-                // no work to do so just bow out
-                if (DEBUG) {
-                    Slog.i(TAG, "Backup queue empty; doing nothing");
-                }
-                return false;
-            }
-
-            // At this point we know that we have work to do, but possibly not right now.
+            // At this point we think that we have work to do, but possibly not right now.
             // Any exit without actually running backups will also require that we
             // reschedule the job.
             boolean runBackup = true;
             boolean headBusy;
 
             do {
+                // Recheck each time, because culling due to ineligibility may
+                // have emptied the queue.
+                if (mFullBackupQueue.size() == 0) {
+                    // no work to do so just bow out
+                    if (DEBUG) {
+                        Slog.i(TAG, "Backup queue empty; doing nothing");
+                    }
+                    runBackup = false;
+                    break;
+                }
+
                 headBusy = false;
 
                 if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
@@ -4744,6 +4777,19 @@
 
                     try {
                         PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+                        if (!appGetsFullBackup(appInfo)) {
+                            // The head app isn't supposed to get full-data backups [any more];
+                            // so we cull it and force a loop around to consider the new head
+                            // app.
+                            if (MORE_DEBUG) {
+                                Slog.i(TAG, "Culling package " + entry.packageName
+                                        + " in full-backup queue but not eligible");
+                            }
+                            mFullBackupQueue.remove(0);
+                            headBusy = true; // force the while() condition
+                            continue;
+                        }
+
                         headBusy = mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
 
                         if (headBusy) {
@@ -4762,7 +4808,6 @@
                             enqueueFullBackup(entry.packageName,
                                     nextEligible - MIN_FULL_BACKUP_INTERVAL);
                         }
-
                     } catch (NameNotFoundException nnf) {
                         // So, we think we want to back this up, but it turns out the package
                         // in question is no longer installed.  We want to drop it from the
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index c1a082b..499b706 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -114,13 +114,6 @@
     private static final int SERVICE_IBLUETOOTH = 1;
     private static final int SERVICE_IBLUETOOTHGATT = 2;
 
-    private static final String[] DEVICE_TYPE_NAMES = new String[] {
-            "???",
-            "BR/EDR",
-            "LE",
-            "DUAL"
-        };
-
     private final Context mContext;
     private static int mBleAppCount = 0;
 
@@ -131,6 +124,7 @@
     private final ContentResolver mContentResolver;
     private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
     private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
+    private IBinder mBluetoothBinder;
     private IBluetooth mBluetooth;
     private IBluetoothGatt mBluetoothGatt;
     private boolean mBinding;
@@ -242,6 +236,7 @@
 
         mContext = context;
         mBluetooth = null;
+        mBluetoothBinder = null;
         mBluetoothGatt = null;
         mBinding = false;
         mUnbinding = false;
@@ -678,6 +673,7 @@
                 }
 
                 if (DBG) Log.d(TAG, "Sending unbind request.");
+                mBluetoothBinder = null;
                 mBluetooth = null;
                 //Unbind
                 mContext.unbindService(mConnection);
@@ -1166,6 +1162,7 @@
                         mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
 
                         mBinding = false;
+                        mBluetoothBinder = service;
                         mBluetooth = IBluetooth.Stub.asInterface(service);
 
                         try {
@@ -1674,44 +1671,15 @@
     }
 
     @Override
-    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
-
-        writer.println("Bluetooth Status");
-        writer.println("  enabled: " + mEnable);
-        writer.println("  state: " + mState);
-        writer.println("  address: " + mAddress);
-        writer.println("  name: " + mName + "\n");
-        writer.flush();
-
-        if (mBluetooth == null) {
-            writer.println("Bluetooth Service not connected");
-        } else {
-            ParcelFileDescriptor pfd = null;
-            try {
-                writer.println("Bonded devices:");
-                for (BluetoothDevice device : mBluetooth.getBondedDevices()) {
-                    writer.println("  " + device.getAddress() +
-                            " [" + DEVICE_TYPE_NAMES[device.getType()] + "] " +
-                            device.getName());
-                }
-                writer.flush();
-
-                pfd = ParcelFileDescriptor.dup(fd);
-                mBluetooth.dump(pfd);
-            } catch (RemoteException re) {
-                writer.println("RemoteException while calling Bluetooth Service");
-            } catch (IOException ioe) {
-                writer.println("IOException attempting to dup() fd");
-            } finally {
-                if (pfd != null) {
-                    try {
-                        pfd.close();
-                    } catch (IOException ioe) {
-                        writer.println("IOException attempting to close() fd");
-                    }
-                }
-            }
+    public void dump(FileDescriptor fd, PrintWriter writer, String args[]) {
+      if (mBluetoothBinder == null) {
+        writer.println("Bluetooth Service not connected");
+      } else {
+        try {
+          mBluetoothBinder.dump(fd, args);
+        } catch (RemoteException re) {
+          writer.println("RemoteException while calling Bluetooth Service");
         }
+      }
     }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9927fd6..3c13630 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -87,6 +87,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -2686,6 +2687,21 @@
                 mTethering.getUpstreamIfaceTypes().length != 0);
     }
 
+    public void startTethering(int type, ResultReceiver receiver,
+            boolean showProvisioningUi) {
+        ConnectivityManager.enforceTetherChangePermission(mContext);
+        if (!isTetheringSupported()) {
+            receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
+            return;
+        }
+        mTethering.startTethering(type, receiver, showProvisioningUi);
+    }
+
+    public void stopTethering(int type) {
+        ConnectivityManager.enforceTetherChangePermission(mContext);
+        mTethering.stopTethering(type);
+    }
+
     // Called when we lose the default network and have no replacement yet.
     // This will automatically be cleared after X seconds or a new default network
     // becomes CONNECTED, whichever happens first.  The timer is started by the
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index dd19c6a..95f5734 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -186,7 +186,6 @@
 
     private final Handler mFgHandler;
     private final Handler mDaemonHandler;
-    private final PhoneStateListener mPhoneStateListener;
 
     private IBatteryStats mBatteryStats;
 
@@ -283,22 +282,6 @@
 
         mDaemonHandler = new Handler(FgThread.get().getLooper());
 
-        mPhoneStateListener = new PhoneStateListener(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                mDaemonHandler.getLooper()) {
-            @Override
-            public void onDataConnectionRealTimeInfoChanged(
-                    DataConnectionRealTimeInfo dcRtInfo) {
-                if (DBG) Slog.d(TAG, "onDataConnectionRealTimeInfoChanged: " + dcRtInfo);
-                notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
-                        dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
-            }
-        };
-        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
-        if (tm != null) {
-            tm.listen(mPhoneStateListener,
-                    PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO);
-        }
-
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
     }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 19a4851..820551d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -34,7 +34,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.telephony.CellLocation;
-import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 import android.telephony.SubscriptionManager;
@@ -179,8 +178,6 @@
 
     private int mDefaultPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
 
-    private DataConnectionRealTimeInfo mDcRtInfo = new DataConnectionRealTimeInfo();
-
     private int mRingingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
 
     private int mForegroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -262,7 +259,8 @@
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0));
             } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) {
                 Integer newDefaultSubIdObj = new Integer(intent.getIntExtra(
-                        PhoneConstants.SUBSCRIPTION_KEY, SubscriptionManager.getDefaultSubId()));
+                        PhoneConstants.SUBSCRIPTION_KEY,
+                        SubscriptionManager.getDefaultSubscriptionId()));
                 int newDefaultPhoneId = intent.getIntExtra(PhoneConstants.SLOT_KEY,
                     SubscriptionManager.getPhoneId(mDefaultSubId));
                 if (DBG) {
@@ -624,13 +622,6 @@
                             remove(r.binder);
                         }
                     }
-                    if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) {
-                        try {
-                            r.callback.onDataConnectionRealTimeInfoChanged(mDcRtInfo);
-                        } catch (RemoteException ex) {
-                            remove(r.binder);
-                        }
-                    }
                     if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
                         try {
                             r.callback.onPreciseCallStateChanged(mPreciseCallState);
@@ -921,31 +912,6 @@
         }
     }
 
-    public void notifyDataConnectionRealTimeInfo(DataConnectionRealTimeInfo dcRtInfo) {
-        if (!checkNotifyPermission("notifyDataConnectionRealTimeInfo()")) {
-            return;
-        }
-
-        synchronized (mRecords) {
-            mDcRtInfo = dcRtInfo;
-            for (Record r : mRecords) {
-                if (validateEventsAndUserLocked(r,
-                        PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO)) {
-                    try {
-                        if (DBG_LOC) {
-                            log("notifyDataConnectionRealTimeInfo: mDcRtInfo="
-                                    + mDcRtInfo + " r=" + r);
-                        }
-                        r.callback.onDataConnectionRealTimeInfoChanged(mDcRtInfo);
-                    } catch (RemoteException ex) {
-                        mRemoveList.add(r.binder);
-                    }
-                }
-            }
-            handleRemoveListLocked();
-        }
-    }
-
     @Override
     public void notifyMessageWaitingChangedForPhoneId(int phoneId, int subId, boolean mwi) {
         if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
@@ -1370,7 +1336,6 @@
                 pw.println("  mCellLocation=" + mCellLocation[i]);
                 pw.println("  mCellInfo=" + mCellInfo.get(i));
             }
-            pw.println("  mDcRtInfo=" + mDcRtInfo);
             pw.println("registrations: count=" + recordCount);
             for (Record r : mRecords) {
                 pw.println("  " + r);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2683be6..d0006aa 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -4266,21 +4266,6 @@
         }
     }
 
-    private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
-        for (String perm : permissions) {
-            if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "  caller uid " + callingUid + " has " + perm);
-                }
-                final int opCode = AppOpsManager.permissionToOpCode(perm);
-                if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
-                        opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
 
     private int handleIncomingUser(int userId) {
         try {
@@ -4355,12 +4340,50 @@
 
     private List<String> getTypesVisibleToCaller(int callingUid, int userId,
             String opPackageName) {
-        boolean isPermitted =
-                isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
-                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
+        List<String> permissionsToCheck = new ArrayList<String>(2);
+        permissionsToCheck.add(Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
+        try {
+            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
+                    opPackageName, 0 /* flags */);
+            /*
+             * At or before SDK 23, clients discover all the accounts in their
+             * user profile (via AccountManager.getAccounts(...)) by declaring
+             * the GET_ACCOUNTS permission.
+             *
+             * After SDK 23 the GET_ACCOUNTS permission is deprecated.  Instead
+             * apps will be able to retrieve those accounts managed by
+             * authenticators sharing a package signature without any special
+             * permissions. The only clients able to discover all the accounts
+             * on the device will be those with the GET_ACCOUNTS_PRVILEGED
+             * system permission.
+             */
+            if (23 >= appInfo.targetSdkVersion) {
+                permissionsToCheck.add(Manifest.permission.GET_ACCOUNTS);
+            }
+        } catch (NameNotFoundException e) {
+            // No application associated with the specified package.
+            Log.w(TAG, "No application associated with package: " + opPackageName);
+        }
+        boolean isPermitted = isPermitted(opPackageName, callingUid, permissionsToCheck);
         return getTypesForCaller(callingUid, userId, isPermitted);
     }
 
+    private boolean isPermitted(String opPackageName, int callingUid, List<String> permissions) {
+        for (String perm : permissions) {
+            if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "  caller uid " + callingUid + " has " + perm);
+                }
+                final int opCode = AppOpsManager.permissionToOpCode(perm);
+                if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
+                        opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private List<String> getTypesManagedByCaller(int callingUid, int userId) {
         return getTypesForCaller(callingUid, userId, false);
     }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7ba6338..8c0ec78 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2775,7 +2775,7 @@
         }
 
         if (anrMessage != null) {
-            mAm.appNotResponding(proc, null, null, false, anrMessage);
+            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1434e5e..2c55ee2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -203,7 +203,6 @@
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.view.Display;
@@ -278,7 +277,6 @@
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
@@ -393,7 +391,7 @@
     // The flags that are set for all calls we make to the package manager.
     static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
 
-    private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+    static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
 
     static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
 
@@ -599,6 +597,12 @@
 
     final UserController mUserController;
 
+    final AppErrors mAppErrors;
+
+    public boolean canShowErrorDialogs() {
+        return mShowDialogs && !mSleeping && !mShuttingDown;
+    }
+
     public class PendingAssistExtras extends Binder implements Runnable {
         public final ActivityRecord activity;
         public final Bundle extras;
@@ -669,38 +673,6 @@
     ProcessRecord mHeavyWeightProcess = null;
 
     /**
-     * The last time that various processes have crashed.
-     */
-    final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
-
-    /**
-     * Information about a process that is currently marked as bad.
-     */
-    static final class BadProcessInfo {
-        BadProcessInfo(long time, String shortMsg, String longMsg, String stack) {
-            this.time = time;
-            this.shortMsg = shortMsg;
-            this.longMsg = longMsg;
-            this.stack = stack;
-        }
-
-        final long time;
-        final String shortMsg;
-        final String longMsg;
-        final String stack;
-    }
-
-    /**
-     * Set of applications that we consider to be bad, and will reject
-     * incoming broadcasts from (which the user has no control over).
-     * Processes are added to this set when they have crashed twice within
-     * a minimum amount of time; they are removed from it when they are
-     * later restarted (hopefully due to some user action).  The value is the
-     * time it was added to the list.
-     */
-    final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<BadProcessInfo>();
-
-    /**
      * All of the processes we currently have running organized by pid.
      * The keys are the pid running the application.
      *
@@ -1352,8 +1324,6 @@
     final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
     final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
 
-    ArraySet<String> mAppsNotReportingCrashes;
-
     /**
      * Runtime CPU use collection thread.  This object's lock is used to
      * perform synchronization with the thread (notifying it to run).
@@ -1473,6 +1443,7 @@
     static final int LOG_STACK_STATE = 62;
     static final int VR_MODE_CHANGE_MSG = 63;
     static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64;
+    static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1511,80 +1482,11 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
             case SHOW_ERROR_UI_MSG: {
-                HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
-                boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
-                        Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-                synchronized (ActivityManagerService.this) {
-                    ProcessRecord proc = (ProcessRecord)data.get("app");
-                    AppErrorResult res = (AppErrorResult) data.get("result");
-                    if (proc != null && proc.crashDialog != null) {
-                        Slog.e(TAG, "App already has crash dialog: " + proc);
-                        if (res != null) {
-                            res.set(0);
-                        }
-                        return;
-                    }
-                    boolean isBackground = (UserHandle.getAppId(proc.uid)
-                            >= Process.FIRST_APPLICATION_UID
-                            && proc.pid != MY_PID);
-                    for (int userId : mUserController.getCurrentProfileIdsLocked()) {
-                        isBackground &= (proc.userId != userId);
-                    }
-                    if (isBackground && !showBackground) {
-                        Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
-                        if (res != null) {
-                            res.set(0);
-                        }
-                        return;
-                    }
-                    final boolean crashSilenced = mAppsNotReportingCrashes != null &&
-                            mAppsNotReportingCrashes.contains(proc.info.packageName);
-                    if (mShowDialogs && !mSleeping && !mShuttingDown && !crashSilenced) {
-                        Dialog d = new AppErrorDialog(mContext,
-                                ActivityManagerService.this, res, proc);
-                        d.show();
-                        proc.crashDialog = d;
-                    } else {
-                        // The device is asleep, so just pretend that the user
-                        // saw a crash dialog and hit "force quit".
-                        if (res != null) {
-                            res.set(0);
-                        }
-                    }
-                }
-
+                mAppErrors.handleShowAppErrorUi(msg);
                 ensureBootCompleted();
             } break;
             case SHOW_NOT_RESPONDING_UI_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
-                    ProcessRecord proc = (ProcessRecord)data.get("app");
-                    if (proc != null && proc.anrDialog != null) {
-                        Slog.e(TAG, "App already has anr dialog: " + proc);
-                        return;
-                    }
-
-                    Intent intent = new Intent("android.intent.action.ANR");
-                    if (!mProcessesReady) {
-                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                                | Intent.FLAG_RECEIVER_FOREGROUND);
-                    }
-                    broadcastIntentLocked(null, null, intent,
-                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
-
-                    if (mShowDialogs) {
-                        Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
-                                mContext, proc, (ActivityRecord)data.get("activity"),
-                                msg.arg1 != 0);
-                        d.show();
-                        proc.anrDialog = d;
-                    } else {
-                        // Just kill the app if there is no dialog to be shown.
-                        killAppAtUsersRequest(proc, null);
-                    }
-                }
-
+                mAppErrors.handleShowAnrUi(msg);
                 ensureBootCompleted();
             } break;
             case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
@@ -2056,6 +1958,20 @@
                 }
                 break;
             }
+            case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: {
+                synchronized (ActivityManagerService.this) {
+                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+                        try {
+                            // Make a one-way callback to the listener
+                            mTaskStackListeners.getBroadcastItem(i).onPinnedActivityRestartAttempt();
+                        } catch (RemoteException e){
+                            // Handled by the RemoteCallbackList
+                        }
+                    }
+                    mTaskStackListeners.finishBroadcast();
+                }
+                break;
+            }
             case NOTIFY_CLEARTEXT_NETWORK_MSG: {
                 final int uid = msg.arg1;
                 final byte[] firstPacket = (byte[]) msg.obj;
@@ -2495,6 +2411,7 @@
 
         mServices = new ActiveServices(this);
         mProviderMap = new ProviderMap(this);
+        mAppErrors = new AppErrors(mContext, this);
 
         // TODO: Move creation of battery stats service outside of activity manager service.
         File dataDir = Environment.getDataDirectory();
@@ -3013,7 +2930,7 @@
         return index;
     }
 
-    private static void killProcessGroup(int uid, int pid) {
+    static void killProcessGroup(int uid, int pid) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
         Process.killProcessGroup(uid, pid);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -3335,7 +3252,7 @@
             if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
                 // If we are in the background, then check to see if this process
                 // is bad.  If so, we will just silently fail.
-                if (mBadProcesses.get(info.processName, info.uid) != null) {
+                if (mAppErrors.isBadProcessLocked(info)) {
                     if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
                             + "/" + info.processName);
                     return null;
@@ -3347,12 +3264,12 @@
                 // if it had been bad.
                 if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
                         + "/" + info.processName);
-                mProcessCrashTimes.remove(info.processName, info.uid);
-                if (mBadProcesses.get(info.processName, info.uid) != null) {
+                mAppErrors.resetProcessCrashTimeLocked(info);
+                if (mAppErrors.isBadProcessLocked(info)) {
                     EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
                             UserHandle.getUserId(info.uid), info.uid,
                             info.processName);
-                    mBadProcesses.remove(info.processName, info.uid);
+                    mAppErrors.clearBadProcessLocked(info);
                     if (app != null) {
                         app.bad = false;
                     }
@@ -4760,46 +4677,7 @@
         }
 
         synchronized(this) {
-            ProcessRecord proc = null;
-
-            // Figure out which process to kill.  We don't trust that initialPid
-            // still has any relation to current pids, so must scan through the
-            // list.
-            synchronized (mPidsSelfLocked) {
-                for (int i=0; i<mPidsSelfLocked.size(); i++) {
-                    ProcessRecord p = mPidsSelfLocked.valueAt(i);
-                    if (p.uid != uid) {
-                        continue;
-                    }
-                    if (p.pid == initialPid) {
-                        proc = p;
-                        break;
-                    }
-                    if (p.pkgList.containsKey(packageName)) {
-                        proc = p;
-                    }
-                }
-            }
-
-            if (proc == null) {
-                Slog.w(TAG, "crashApplication: nothing for uid=" + uid
-                        + " initialPid=" + initialPid
-                        + " packageName=" + packageName);
-                return;
-            }
-
-            if (proc.thread != null) {
-                if (proc.pid == Process.myPid()) {
-                    Log.w(TAG, "crashApplication: trying to crash self!");
-                    return;
-                }
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    proc.thread.scheduleCrash(message);
-                } catch (RemoteException e) {
-                }
-                Binder.restoreCallingIdentity(ident);
-            }
+            mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, message);
         }
     }
 
@@ -5280,169 +5158,6 @@
         }
     }
 
-    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
-            ActivityRecord parent, boolean aboveSystem, final String annotation) {
-        ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
-        SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
-
-        if (mController != null) {
-            try {
-                // 0 == continue, -1 = kill process immediately
-                int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);
-                if (res < 0 && app.pid != MY_PID) {
-                    app.kill("anr", true);
-                }
-            } catch (RemoteException e) {
-                mController = null;
-                Watchdog.getInstance().setActivityController(null);
-            }
-        }
-
-        long anrTime = SystemClock.uptimeMillis();
-        if (MONITOR_CPU_USAGE) {
-            updateCpuStatsNow();
-        }
-
-        synchronized (this) {
-            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
-            if (mShuttingDown) {
-                Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
-                return;
-            } else if (app.notResponding) {
-                Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
-                return;
-            } else if (app.crashing) {
-                Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
-                return;
-            }
-
-            // In case we come through here for the same app before completing
-            // this one, mark as anring now so we will bail out.
-            app.notResponding = true;
-
-            // Log the ANR to the event log.
-            EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
-                    app.processName, app.info.flags, annotation);
-
-            // Dump thread traces as quickly as we can, starting with "interesting" processes.
-            firstPids.add(app.pid);
-
-            int parentPid = app.pid;
-            if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;
-            if (parentPid != app.pid) firstPids.add(parentPid);
-
-            if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
-
-            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-                ProcessRecord r = mLruProcesses.get(i);
-                if (r != null && r.thread != null) {
-                    int pid = r.pid;
-                    if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
-                        if (r.persistent) {
-                            firstPids.add(pid);
-                        } else {
-                            lastPids.put(pid, Boolean.TRUE);
-                        }
-                    }
-                }
-            }
-        }
-
-        // Log the ANR to the main log.
-        StringBuilder info = new StringBuilder();
-        info.setLength(0);
-        info.append("ANR in ").append(app.processName);
-        if (activity != null && activity.shortComponentName != null) {
-            info.append(" (").append(activity.shortComponentName).append(")");
-        }
-        info.append("\n");
-        info.append("PID: ").append(app.pid).append("\n");
-        if (annotation != null) {
-            info.append("Reason: ").append(annotation).append("\n");
-        }
-        if (parent != null && parent != activity) {
-            info.append("Parent: ").append(parent.shortComponentName).append("\n");
-        }
-
-        final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
-
-        File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
-                NATIVE_STACKS_OF_INTEREST);
-
-        String cpuInfo = null;
-        if (MONITOR_CPU_USAGE) {
-            updateCpuStatsNow();
-            synchronized (mProcessCpuTracker) {
-                cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
-            }
-            info.append(processCpuTracker.printCurrentLoad());
-            info.append(cpuInfo);
-        }
-
-        info.append(processCpuTracker.printCurrentState(anrTime));
-
-        Slog.e(TAG, info.toString());
-        if (tracesFile == null) {
-            // There is no trace file, so dump (only) the alleged culprit's threads to the log
-            Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
-        }
-
-        addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
-                cpuInfo, tracesFile, null);
-
-        if (mController != null) {
-            try {
-                // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
-                int res = mController.appNotResponding(app.processName, app.pid, info.toString());
-                if (res != 0) {
-                    if (res < 0 && app.pid != MY_PID) {
-                        app.kill("anr", true);
-                    } else {
-                        synchronized (this) {
-                            mServices.scheduleServiceTimeoutLocked(app);
-                        }
-                    }
-                    return;
-                }
-            } catch (RemoteException e) {
-                mController = null;
-                Watchdog.getInstance().setActivityController(null);
-            }
-        }
-
-        // Unless configured otherwise, swallow ANRs in background processes & kill the process.
-        boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-
-        synchronized (this) {
-            mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
-
-            if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
-                app.kill("bg anr", true);
-                return;
-            }
-
-            // Set the app's notResponding state, and look up the errorReportReceiver
-            makeAppNotRespondingLocked(app,
-                    activity != null ? activity.shortComponentName : null,
-                    annotation != null ? "ANR " + annotation : "ANR",
-                    info.toString());
-
-            // Bring up the infamous App Not Responding dialog
-            Message msg = Message.obtain();
-            HashMap<String, Object> map = new HashMap<String, Object>();
-            msg.what = SHOW_NOT_RESPONDING_UI_MSG;
-            msg.obj = map;
-            msg.arg1 = aboveSystem ? 1 : 0;
-            map.put("app", app);
-            if (activity != null) {
-                map.put("activity", activity);
-            }
-
-            mUiHandler.sendMessage(msg);
-        }
-    }
-
     final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
         if (!mLaunchWarningShown) {
             mLaunchWarningShown = true;
@@ -6065,33 +5780,7 @@
                 Slog.i(TAG, "Force stopping u" + userId + ": " + reason);
             }
 
-            final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
-            for (int ip = pmap.size() - 1; ip >= 0; ip--) {
-                SparseArray<Long> ba = pmap.valueAt(ip);
-                for (i = ba.size() - 1; i >= 0; i--) {
-                    boolean remove = false;
-                    final int entUid = ba.keyAt(i);
-                    if (packageName != null) {
-                        if (userId == UserHandle.USER_ALL) {
-                            if (UserHandle.getAppId(entUid) == appId) {
-                                remove = true;
-                            }
-                        } else {
-                            if (entUid == UserHandle.getUid(userId, appId)) {
-                                remove = true;
-                            }
-                        }
-                    } else if (UserHandle.getUserId(entUid) == userId) {
-                        remove = true;
-                    }
-                    if (remove) {
-                        ba.removeAt(i);
-                    }
-                }
-                if (ba.size() == 0) {
-                    pmap.removeAt(ip);
-                }
-            }
+            mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
         }
 
         boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
@@ -6256,7 +5945,7 @@
         }
     }
 
-    private final boolean removeProcessLocked(ProcessRecord app,
+    boolean removeProcessLocked(ProcessRecord app,
             boolean callerWillRestart, boolean allowRestart, String reason) {
         final String name = app.processName;
         final int uid = app.uid;
@@ -8989,6 +8678,11 @@
                     continue;
                 }
 
+                if (tr.realActivitySuspended) {
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
+                    continue;
+                }
+
                 // Return the entry if desired by the caller.  We always return
                 // the first entry, because callers always expect this to be the
                 // foreground app.  We may filter others if the caller has
@@ -10919,7 +10613,7 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            appNotResponding(host, null, null, false, "ContentProvider not responding");
+            mAppErrors.appNotResponding(host, null, null, false, "ContentProvider not responding");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -11284,6 +10978,16 @@
         mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG).sendToTarget();
     }
 
+    /**
+     * Notifies all listeners when an attempt was made to start an an activity that is already
+     * running in the pinned stack and the activity was not actually started, but the task is
+     * either brought to the front or a new Intent is delivered to it.
+     */
+    void notifyPinnedActivityRestartAttemptLocked() {
+        mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+        mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG).sendToTarget();
+    }
+
     @Override
     public void notifyCleartextNetwork(int uid, byte[] firstPacket) {
         mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget();
@@ -11673,7 +11377,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    appNotResponding(proc, activity, parent, aboveSystem, annotation);
+                    mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
                 }
             });
         }
@@ -12467,17 +12171,8 @@
                     com.android.internal.R.dimen.thumbnail_height);
             mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
                     com.android.internal.R.string.config_defaultPictureInPictureBounds));
-            final String appsNotReportingCrashes = res.getString(
-                    com.android.internal.R.string.config_appsNotReportingCrashes);
-            if (appsNotReportingCrashes != null) {
-                final String[] split = appsNotReportingCrashes.split(",");
-                if (split.length > 0) {
-                    mAppsNotReportingCrashes = new ArraySet<>();
-                    for (int i = 0; i < split.length; i++) {
-                        mAppsNotReportingCrashes.add(split[i]);
-                    }
-                }
-            }
+            mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
+                    com.android.internal.R.string.config_appsNotReportingCrashes));
         }
     }
 
@@ -12882,174 +12577,12 @@
         }
     }
 
-    private boolean makeAppCrashingLocked(ProcessRecord app,
-            String shortMsg, String longMsg, String stackTrace) {
-        app.crashing = true;
-        app.crashingReport = generateProcessError(app,
-                ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
-        startAppProblemLocked(app);
-        app.stopFreezingAllLocked();
-        return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace);
-    }
-
-    private void makeAppNotRespondingLocked(ProcessRecord app,
-            String activity, String shortMsg, String longMsg) {
-        app.notResponding = true;
-        app.notRespondingReport = generateProcessError(app,
-                ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
-                activity, shortMsg, longMsg, null);
-        startAppProblemLocked(app);
-        app.stopFreezingAllLocked();
-    }
-
-    /**
-     * Generate a process error record, suitable for attachment to a ProcessRecord.
-     *
-     * @param app The ProcessRecord in which the error occurred.
-     * @param condition Crashing, Application Not Responding, etc.  Values are defined in
-     *                      ActivityManager.AppErrorStateInfo
-     * @param activity The activity associated with the crash, if known.
-     * @param shortMsg Short message describing the crash.
-     * @param longMsg Long message describing the crash.
-     * @param stackTrace Full crash stack trace, may be null.
-     *
-     * @return Returns a fully-formed AppErrorStateInfo record.
-     */
-    private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
-            int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
-        ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
-
-        report.condition = condition;
-        report.processName = app.processName;
-        report.pid = app.pid;
-        report.uid = app.info.uid;
-        report.tag = activity;
-        report.shortMsg = shortMsg;
-        report.longMsg = longMsg;
-        report.stackTrace = stackTrace;
-
-        return report;
-    }
-
     void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) {
         synchronized (this) {
-            app.crashing = false;
-            app.crashingReport = null;
-            app.notResponding = false;
-            app.notRespondingReport = null;
-            if (app.anrDialog == fromDialog) {
-                app.anrDialog = null;
-            }
-            if (app.waitDialog == fromDialog) {
-                app.waitDialog = null;
-            }
-            if (app.pid > 0 && app.pid != MY_PID) {
-                handleAppCrashLocked(app, "user-terminated" /*reason*/,
-                        null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/);
-                app.kill("user request after error", true);
-            }
+            mAppErrors.killAppAtUserRequestLocked(app, fromDialog);
         }
     }
 
-    private boolean handleAppCrashLocked(ProcessRecord app, String reason,
-            String shortMsg, String longMsg, String stackTrace) {
-        long now = SystemClock.uptimeMillis();
-
-        Long crashTime;
-        if (!app.isolated) {
-            crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
-        } else {
-            crashTime = null;
-        }
-        if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
-            // This process loses!
-            Slog.w(TAG, "Process " + app.info.processName
-                    + " has crashed too many times: killing!");
-            EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
-                    app.userId, app.info.processName, app.uid);
-            mStackSupervisor.handleAppCrashLocked(app);
-            if (!app.persistent) {
-                // We don't want to start this process again until the user
-                // explicitly does so...  but for persistent process, we really
-                // need to keep it running.  If a persistent process is actually
-                // repeatedly crashing, then badness for everyone.
-                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
-                        app.info.processName);
-                if (!app.isolated) {
-                    // XXX We don't have a way to mark isolated processes
-                    // as bad, since they don't have a peristent identity.
-                    mBadProcesses.put(app.info.processName, app.uid,
-                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
-                    mProcessCrashTimes.remove(app.info.processName, app.uid);
-                }
-                app.bad = true;
-                app.removed = true;
-                // Don't let services in this process be restarted and potentially
-                // annoy the user repeatedly.  Unless it is persistent, since those
-                // processes run critical code.
-                removeProcessLocked(app, false, false, "crash");
-                mStackSupervisor.resumeFocusedStackTopActivityLocked();
-                return false;
-            }
-            mStackSupervisor.resumeFocusedStackTopActivityLocked();
-        } else {
-            mStackSupervisor.finishTopRunningActivityLocked(app, reason);
-        }
-
-        // Bump up the crash count of any services currently running in the proc.
-        for (int i=app.services.size()-1; i>=0; i--) {
-            // Any services running in the application need to be placed
-            // back in the pending list.
-            ServiceRecord sr = app.services.valueAt(i);
-            sr.crashCount++;
-        }
-
-        // If the crashing process is what we consider to be the "home process" and it has been
-        // replaced by a third-party app, clear the package preferred activities from packages
-        // with a home activity running in the process to prevent a repeatedly crashing app
-        // from blocking the user to manually clear the list.
-        final ArrayList<ActivityRecord> activities = app.activities;
-        if (app == mHomeProcess && activities.size() > 0
-                    && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.isHomeActivity()) {
-                    Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
-                    try {
-                        ActivityThread.getPackageManager()
-                                .clearPackagePreferredActivities(r.packageName);
-                    } catch (RemoteException c) {
-                        // pm is in same process, this will never happen.
-                    }
-                }
-            }
-        }
-
-        if (!app.isolated) {
-            // XXX Can't keep track of crash times for isolated processes,
-            // because they don't have a perisistent identity.
-            mProcessCrashTimes.put(app.info.processName, app.uid, now);
-        }
-
-        if (app.crashHandler != null) mHandler.post(app.crashHandler);
-        return true;
-    }
-
-    void startAppProblemLocked(ProcessRecord app) {
-        // If this app is not running under the current user, then we
-        // can't give it a report button because that would require
-        // launching the report UI under a different user.
-        app.errorReportReceiver = null;
-
-        for (int userId : mUserController.getCurrentProfileIdsLocked()) {
-            if (app.userId == userId) {
-                app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
-                        mContext, app.info.packageName, app.info.flags);
-            }
-        }
-        skipCurrentReceiverLocked(app);
-    }
-
     void skipCurrentReceiverLocked(ProcessRecord app) {
         for (BroadcastQueue queue : mBroadcastQueues) {
             queue.skipCurrentReceiverLocked(app);
@@ -13085,7 +12618,7 @@
 
         addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
 
-        crashApplication(r, crashInfo);
+        mAppErrors.crashApplication(r, crashInfo);
     }
 
     public void handleApplicationStrictModeViolation(
@@ -13296,7 +12829,7 @@
         if (r != null && r.pid != Process.myPid() &&
                 Settings.Global.getInt(mContext.getContentResolver(),
                         Settings.Global.WTF_IS_FATAL, 0) != 0) {
-            crashApplication(r, crashInfo);
+            mAppErrors.crashApplication(r, crashInfo);
             return true;
         } else {
             return false;
@@ -13505,164 +13038,6 @@
         }
     }
 
-    /**
-     * Bring up the "unexpected error" dialog box for a crashing app.
-     * Deal with edge cases (intercepts from instrumented applications,
-     * ActivityController, error intent receivers, that sort of thing).
-     * @param r the application crashing
-     * @param crashInfo describing the failure
-     */
-    private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
-        long timeMillis = System.currentTimeMillis();
-        String shortMsg = crashInfo.exceptionClassName;
-        String longMsg = crashInfo.exceptionMessage;
-        String stackTrace = crashInfo.stackTrace;
-        if (shortMsg != null && longMsg != null) {
-            longMsg = shortMsg + ": " + longMsg;
-        } else if (shortMsg != null) {
-            longMsg = shortMsg;
-        }
-
-        AppErrorResult result = new AppErrorResult();
-        synchronized (this) {
-            if (mController != null) {
-                try {
-                    String name = r != null ? r.processName : null;
-                    int pid = r != null ? r.pid : Binder.getCallingPid();
-                    int uid = r != null ? r.info.uid : Binder.getCallingUid();
-                    if (!mController.appCrashed(name, pid,
-                            shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
-                        if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
-                                && "Native crash".equals(crashInfo.exceptionClassName)) {
-                            Slog.w(TAG, "Skip killing native crashed app " + name
-                                    + "(" + pid + ") during testing");
-                        } else {
-                            Slog.w(TAG, "Force-killing crashed app " + name
-                                    + " at watcher's request");
-                            if (r != null) {
-                                r.kill("crash", true);
-                            } else {
-                                // Huh.
-                                Process.killProcess(pid);
-                                killProcessGroup(uid, pid);
-                            }
-                        }
-                        return;
-                    }
-                } catch (RemoteException e) {
-                    mController = null;
-                    Watchdog.getInstance().setActivityController(null);
-                }
-            }
-
-            final long origId = Binder.clearCallingIdentity();
-
-            // If this process is running instrumentation, finish it.
-            if (r != null && r.instrumentationClass != null) {
-                Slog.w(TAG, "Error in app " + r.processName
-                      + " running instrumentation " + r.instrumentationClass + ":");
-                if (shortMsg != null) Slog.w(TAG, "  " + shortMsg);
-                if (longMsg != null) Slog.w(TAG, "  " + longMsg);
-                Bundle info = new Bundle();
-                info.putString("shortMsg", shortMsg);
-                info.putString("longMsg", longMsg);
-                finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info);
-                Binder.restoreCallingIdentity(origId);
-                return;
-            }
-
-            // Log crash in battery stats.
-            if (r != null) {
-                mBatteryStatsService.noteProcessCrash(r.processName, r.uid);
-            }
-
-            // If we can't identify the process or it's already exceeded its crash quota,
-            // quit right away without showing a crash dialog.
-            if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {
-                Binder.restoreCallingIdentity(origId);
-                return;
-            }
-
-            Message msg = Message.obtain();
-            msg.what = SHOW_ERROR_UI_MSG;
-            HashMap data = new HashMap();
-            data.put("result", result);
-            data.put("app", r);
-            msg.obj = data;
-            mUiHandler.sendMessage(msg);
-
-            Binder.restoreCallingIdentity(origId);
-        }
-
-        int res = result.get();
-
-        Intent appErrorIntent = null;
-        synchronized (this) {
-            if (r != null && !r.isolated) {
-                // XXX Can't keep track of crash time for isolated processes,
-                // since they don't have a persistent identity.
-                mProcessCrashTimes.put(r.info.processName, r.uid,
-                        SystemClock.uptimeMillis());
-            }
-            if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
-                appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
-            }
-        }
-
-        if (appErrorIntent != null) {
-            try {
-                mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
-            } catch (ActivityNotFoundException e) {
-                Slog.w(TAG, "bug report receiver dissappeared", e);
-            }
-        }
-    }
-
-    Intent createAppErrorIntentLocked(ProcessRecord r,
-            long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
-        ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
-        if (report == null) {
-            return null;
-        }
-        Intent result = new Intent(Intent.ACTION_APP_ERROR);
-        result.setComponent(r.errorReportReceiver);
-        result.putExtra(Intent.EXTRA_BUG_REPORT, report);
-        result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return result;
-    }
-
-    private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r,
-            long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
-        if (r.errorReportReceiver == null) {
-            return null;
-        }
-
-        if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
-            return null;
-        }
-
-        ApplicationErrorReport report = new ApplicationErrorReport();
-        report.packageName = r.info.packageName;
-        report.installerPackageName = r.errorReportReceiver.getPackageName();
-        report.processName = r.processName;
-        report.time = timeMillis;
-        report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
-
-        if (r.crashing || r.forceCrashReport) {
-            report.type = ApplicationErrorReport.TYPE_CRASH;
-            report.crashInfo = crashInfo;
-        } else if (r.notResponding) {
-            report.type = ApplicationErrorReport.TYPE_ANR;
-            report.anrInfo = new ApplicationErrorReport.AnrInfo();
-
-            report.anrInfo.activity = r.notRespondingReport.tag;
-            report.anrInfo.cause = r.notRespondingReport.shortMsg;
-            report.anrInfo.info = r.notRespondingReport.longMsg;
-        }
-
-        return report;
-    }
-
     public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
         enforceNotIsolatedCaller("getProcessesInErrorState");
         // assume our apps are happy - lazy create the list
@@ -14400,88 +13775,9 @@
 
         needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, dumpPackage);
 
-        if (mProcessCrashTimes.getMap().size() > 0) {
-            boolean printed = false;
-            long now = SystemClock.uptimeMillis();
-            final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
-            final int NP = pmap.size();
-            for (int ip=0; ip<NP; ip++) {
-                String pname = pmap.keyAt(ip);
-                SparseArray<Long> uids = pmap.valueAt(ip);
-                final int N = uids.size();
-                for (int i=0; i<N; i++) {
-                    int puid = uids.keyAt(i);
-                    ProcessRecord r = mProcessNames.get(pname, puid);
-                    if (dumpPackage != null && (r == null
-                            || !r.pkgList.containsKey(dumpPackage))) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (needSep) pw.println();
-                        needSep = true;
-                        pw.println("  Time since processes crashed:");
-                        printed = true;
-                        printedAnything = true;
-                    }
-                    pw.print("    Process "); pw.print(pname);
-                            pw.print(" uid "); pw.print(puid);
-                            pw.print(": last crashed ");
-                            TimeUtils.formatDuration(now-uids.valueAt(i), pw);
-                            pw.println(" ago");
-                }
-            }
-        }
-
-        if (mBadProcesses.getMap().size() > 0) {
-            boolean printed = false;
-            final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
-            final int NP = pmap.size();
-            for (int ip=0; ip<NP; ip++) {
-                String pname = pmap.keyAt(ip);
-                SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
-                final int N = uids.size();
-                for (int i=0; i<N; i++) {
-                    int puid = uids.keyAt(i);
-                    ProcessRecord r = mProcessNames.get(pname, puid);
-                    if (dumpPackage != null && (r == null
-                            || !r.pkgList.containsKey(dumpPackage))) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (needSep) pw.println();
-                        needSep = true;
-                        pw.println("  Bad processes:");
-                        printedAnything = true;
-                    }
-                    BadProcessInfo info = uids.valueAt(i);
-                    pw.print("    Bad process "); pw.print(pname);
-                            pw.print(" uid "); pw.print(puid);
-                            pw.print(": crashed at time "); pw.println(info.time);
-                    if (info.shortMsg != null) {
-                        pw.print("      Short msg: "); pw.println(info.shortMsg);
-                    }
-                    if (info.longMsg != null) {
-                        pw.print("      Long msg: "); pw.println(info.longMsg);
-                    }
-                    if (info.stack != null) {
-                        pw.println("      Stack:");
-                        int lastPos = 0;
-                        for (int pos=0; pos<info.stack.length(); pos++) {
-                            if (info.stack.charAt(pos) == '\n') {
-                                pw.print("        ");
-                                pw.write(info.stack, lastPos, pos-lastPos);
-                                pw.println();
-                                lastPos = pos+1;
-                            }
-                        }
-                        if (lastPos < info.stack.length()) {
-                            pw.print("        ");
-                            pw.write(info.stack, lastPos, info.stack.length()-lastPos);
-                            pw.println();
-                        }
-                    }
-                }
-            }
+        needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
+        if (needSep) {
+            printedAnything = true;
         }
 
         if (dumpPackage == null) {
@@ -17563,6 +16859,8 @@
                 case Intent.ACTION_PACKAGE_CHANGED:
                 case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                 case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+                case Intent.ACTION_PACKAGES_SUSPENDED:
+                case Intent.ACTION_PACKAGES_UNSUSPENDED:
                     // Handle special intents: if this broadcast is from the package
                     // manager about a package being removed, we need to remove all of
                     // its activities from the history stack.
@@ -17643,6 +16941,20 @@
                                 }
                             }
                             break;
+                        case Intent.ACTION_PACKAGES_SUSPENDED:
+                        case Intent.ACTION_PACKAGES_UNSUSPENDED:
+                            final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals(
+                                    intent.getAction());
+                            final String[] packageNames = intent.getStringArrayExtra(
+                                    Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                            final int userHandle = intent.getIntExtra(
+                                    Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
+                            synchronized(ActivityManagerService.this) {
+                                mRecentTasks.onPackagesSuspendedChanged(
+                                        packageNames, suspended, userHandle);
+                            }
+                            break;
                     }
                     break;
                 case Intent.ACTION_PACKAGE_ADDED:
@@ -21128,13 +20440,6 @@
         }
     }
 
-    void stopReportingCrashesLocked(ProcessRecord proc) {
-        if (mAppsNotReportingCrashes == null) {
-            mAppsNotReportingCrashes = new ArraySet<>();
-        }
-        mAppsNotReportingCrashes.add(proc.info.packageName);
-    }
-
     private final class LocalService extends ActivityManagerInternal {
         @Override
         public void onWakefulnessChanged(int wakefulness) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4bac2d6..ef8d230 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3041,42 +3041,44 @@
         mService.updateOomAdjLocked();
     }
 
-    final void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
+    final TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) {
         ActivityRecord r = topRunningActivityLocked();
-        if (r != null && r.app == app) {
-            // If the top running activity is from this crashing
-            // process, then terminate it to avoid getting in a loop.
-            Slog.w(TAG, "  Force finishing activity "
-                    + r.intent.getComponent().flattenToShortString());
-            int taskNdx = mTaskHistory.indexOf(r.task);
-            int activityNdx = r.task.mActivities.indexOf(r);
-            finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
-            // Also terminate any activities below it that aren't yet
-            // stopped, to avoid a situation where one will get
-            // re-start our crashing activity once it gets resumed again.
-            --activityNdx;
-            if (activityNdx < 0) {
-                do {
-                    --taskNdx;
-                    if (taskNdx < 0) {
-                        break;
-                    }
-                    activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
-                } while (activityNdx < 0);
-            }
-            if (activityNdx >= 0) {
-                r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
-                if (r.state == ActivityState.RESUMED
-                        || r.state == ActivityState.PAUSING
-                        || r.state == ActivityState.PAUSED) {
-                    if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
-                        Slog.w(TAG, "  Force finishing activity "
-                                + r.intent.getComponent().flattenToShortString());
-                        finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
-                    }
+        TaskRecord finishedTask = null;
+        if (r == null || r.app != app) {
+            return null;
+        }
+        Slog.w(TAG, "  Force finishing activity "
+                + r.intent.getComponent().flattenToShortString());
+        int taskNdx = mTaskHistory.indexOf(r.task);
+        int activityNdx = r.task.mActivities.indexOf(r);
+        finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
+        finishedTask = r.task;
+        // Also terminate any activities below it that aren't yet
+        // stopped, to avoid a situation where one will get
+        // re-start our crashing activity once it gets resumed again.
+        --activityNdx;
+        if (activityNdx < 0) {
+            do {
+                --taskNdx;
+                if (taskNdx < 0) {
+                    break;
+                }
+                activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
+            } while (activityNdx < 0);
+        }
+        if (activityNdx >= 0) {
+            r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
+            if (r.state == ActivityState.RESUMED
+                    || r.state == ActivityState.PAUSING
+                    || r.state == ActivityState.PAUSED) {
+                if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
+                    Slog.w(TAG, "  Force finishing activity "
+                            + r.intent.getComponent().flattenToShortString());
+                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
                 }
             }
         }
+        return finishedTask;
     }
 
     final void finishVoiceTask(IVoiceInteractionSession session) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1fc674b..8db2f8f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1668,15 +1668,21 @@
         return false;
     }
 
-    void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
+    TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) {
+        TaskRecord finishedTask = null;
+        ActivityStack focusedStack = getFocusedStack();
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             final int numStacks = stacks.size();
             for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
-                stack.finishTopRunningActivityLocked(app, reason);
+                TaskRecord t = stack.finishTopRunningActivityLocked(app, reason);
+                if (stack == focusedStack || finishedTask == null) {
+                    finishedTask = t;
+                }
             }
         }
+        return finishedTask;
     }
 
     void finishVoiceTask(IVoiceInteractionSession session) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 23dc0f6..7b7359f 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -504,18 +504,45 @@
 
         err = startActivityUnchecked(
                 r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, options, inTask);
+        postStartActivityUncheckedProcessing(r, err, stack.mStackId);
+        return err;
+    }
 
-        if (err < 0) {
-            // If someone asked to have the keyguard dismissed on the next
-            // activity start, but we are not actually doing an activity
-            // switch...  just dismiss the keyguard now, because we
-            // probably want to see whatever is behind it.
+    void postStartActivityUncheckedProcessing(
+            ActivityRecord r, int result, int prevFocusedStackId) {
+
+        if (result < START_SUCCESS) {
+            // If someone asked to have the keyguard dismissed on the next activity start,
+            // but we are not actually doing an activity switch...  just dismiss the keyguard now,
+            // because we probably want to see whatever is behind it.
             mSupervisor.notifyActivityDrawnForKeyguard();
-        } else {
-            launchRecentsAppIfNeeded(stack);
+            return;
         }
 
-        return err;
+        int startedActivityStackId = INVALID_STACK_ID;
+        if (r.task != null && r.task.stack != null) {
+            startedActivityStackId = r.task.stack.mStackId;
+        } else if (mTargetStack != null) {
+            startedActivityStackId = mTargetStack.mStackId;
+        }
+
+        if (startedActivityStackId == DOCKED_STACK_ID && prevFocusedStackId == HOME_STACK_ID) {
+            // We launch an activity while being in home stack, which means either launcher or
+            // recents into docked stack. We don't want the launched activity to be alone in a
+            // docked stack, so we want to immediately launch recents too.
+            if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
+            mWindowManager.showRecentApps();
+            return;
+        }
+
+        if (startedActivityStackId == PINNED_STACK_ID
+                && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP)) {
+            // The activity was already running in the pinned stack so it wasn't started, but either
+            // brought to the front or the new intent was delivered to it since it was already in
+            // front. Notify anyone interested in this piece of information.
+            mService.notifyPinnedActivityRestartAttemptLocked();
+            return;
+        }
     }
 
     void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
@@ -985,17 +1012,6 @@
         return START_SUCCESS;
     }
 
-    private void launchRecentsAppIfNeeded(ActivityStack topStack) {
-        if (topStack.mStackId == HOME_STACK_ID && mTargetStack != null
-                && mTargetStack.mStackId == DOCKED_STACK_ID) {
-            // We launch an activity while being in home stack, which means either launcher or
-            // recents into docked stack. We don't want the launched activity to be alone in a
-            // docked stack, so we want to immediately launch recents too.
-            if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
-            mWindowManager.showRecentApps();
-        }
-    }
-
     private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
             boolean doResume, int startFlags, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
@@ -1557,11 +1573,13 @@
 
     final void doPendingActivityLaunchesLocked(boolean doResume) {
         while (!mPendingActivityLaunches.isEmpty()) {
-            PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
-
+            final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+            final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
             try {
-                startActivityUnchecked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                        doResume && mPendingActivityLaunches.isEmpty(), null, null);
+                final int result = startActivityUnchecked(
+                        pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null, null);
+                postStartActivityUncheckedProcessing(
+                        pal.r, result, mSupervisor.mFocusedStack.mStackId);
             } catch (Exception e) {
                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                 pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index c87eae0..b746a4b 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -16,73 +16,74 @@
 
 package com.android.server.am;
 
+import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
 import android.content.Context;
-import android.content.DialogInterface;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.widget.CheckBox;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
-final class AppErrorDialog extends BaseErrorDialog {
+import java.util.List;
+
+import static com.android.server.am.ActivityManagerService.IS_USER_BUILD;
+
+final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener {
     private final ActivityManagerService mService;
     private final AppErrorResult mResult;
     private final ProcessRecord mProc;
+    private final boolean mRepeating;
+
     private CharSequence mName;
 
     // Event 'what' codes
-    static final int FORCE_QUIT = 0;
-    static final int FORCE_QUIT_AND_REPORT = 1;
+    static final int FORCE_QUIT = 1;
+    static final int FORCE_QUIT_AND_REPORT = 2;
+    static final int RESTART = 3;
+    static final int RESET = 4;
+    static final int MUTE = 5;
 
     // 5-minute timeout, then we automatically dismiss the crash dialog
     static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
-    
-    public AppErrorDialog(Context context, ActivityManagerService service,
-            AppErrorResult result, ProcessRecord app) {
-        super(context);
 
+    public AppErrorDialog(Context context, ActivityManagerService service, Data data) {
+        super(context);
         Resources res = context.getResources();
 
         mService = service;
-        mProc = app;
-        mResult = result;
-        if ((app.pkgList.size() == 1) &&
-                (mName = context.getPackageManager().getApplicationLabel(app.info)) != null) {
-            setMessage(res.getString(
-                    com.android.internal.R.string.aerr_application,
-                    mName.toString(), app.info.processName));
+        mProc = data.proc;
+        mResult = data.result;
+        mRepeating = data.repeating;
+        if ((mProc.pkgList.size() == 1) &&
+                (mName = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
+            setTitle(res.getString(
+                    mRepeating ? com.android.internal.R.string.aerr_application_repeated
+                            : com.android.internal.R.string.aerr_application,
+                    mName.toString(), mProc.info.processName));
         } else {
-            mName = app.processName;
-            setMessage(res.getString(
-                    com.android.internal.R.string.aerr_process,
+            mName = mProc.processName;
+            setTitle(res.getString(
+                    mRepeating ? com.android.internal.R.string.aerr_process_repeated
+                            : com.android.internal.R.string.aerr_process,
                     mName.toString()));
         }
 
         setCancelable(false);
 
-        setButton(DialogInterface.BUTTON_POSITIVE,
-                res.getText(com.android.internal.R.string.force_close),
-                mHandler.obtainMessage(FORCE_QUIT));
-
-        if (app.errorReportReceiver != null) {
-            setButton(DialogInterface.BUTTON_NEGATIVE,
-                    res.getText(com.android.internal.R.string.report),
-                    mHandler.obtainMessage(FORCE_QUIT_AND_REPORT));
-        }
-
-        setTitle(res.getText(com.android.internal.R.string.aerr_title));
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
-        attrs.setTitle("Application Error: " + app.info.processName);
+        attrs.setTitle("Application Error: " + mProc.info.processName);
         attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
                 | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
-        if (app.persistent) {
+        if (mProc.persistent) {
             getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
         }
 
@@ -95,38 +96,44 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        if (!ActivityManagerService.IS_USER_BUILD) {
-            FrameLayout frame = (FrameLayout) findViewById(android.R.id.custom);
-            Context context = getContext();
-            LayoutInflater.from(context).inflate(
-                    com.android.internal.R.layout.app_error_dialog_dont_show_again, frame, true);
-            ((TextView) frame.findViewById(com.android.internal.R.id.text)).setText(
-                    context.getResources().getString(
-                            com.android.internal.R.string.aerr_process_silence,
-                            mName.toString()));
-            findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE);
-        }
+        final FrameLayout frame = (FrameLayout) findViewById(android.R.id.custom);
+        final Context context = getContext();
+        LayoutInflater.from(context).inflate(
+                com.android.internal.R.layout.app_error_dialog, frame, true);
+
+        final TextView restart = (TextView) findViewById(com.android.internal.R.id.aerr_restart);
+        restart.setOnClickListener(this);
+        restart.setVisibility(!mRepeating ? View.VISIBLE : View.GONE);
+        final TextView reset = (TextView) findViewById(com.android.internal.R.id.aerr_reset);
+        reset.setOnClickListener(this);
+        reset.setVisibility(mRepeating ? View.VISIBLE : View.GONE);
+        final TextView report = (TextView) findViewById(com.android.internal.R.id.aerr_report);
+        report.setOnClickListener(this);
+        final boolean hasReceiver = mProc.errorReportReceiver != null;
+        report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE);
+        final TextView close = (TextView) findViewById(com.android.internal.R.id.aerr_close);
+        close.setOnClickListener(this);
+        final TextView mute = (TextView) findViewById(com.android.internal.R.id.aerr_mute);
+        mute.setOnClickListener(this);
+        mute.setVisibility(!IS_USER_BUILD ? View.VISIBLE : View.GONE);
+
+        findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE);
     }
 
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
-            View view = findViewById(com.android.internal.R.id.checkbox);
-            final boolean stopReporting = view != null && ((CheckBox) view).isChecked();
+            final int result = msg.what;
+
             synchronized (mService) {
                 if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
                     mProc.crashDialog = null;
                 }
-                if (stopReporting) {
-                    mService.stopReportingCrashesLocked(mProc);
-                }
             }
-            mResult.set(msg.what);
+            mResult.set(result);
 
             // Make sure we don't have time timeout still hanging around.
             removeMessages(FORCE_QUIT);
 
-            // If this is a timeout we won't be automatically closed, so go
-            // ahead and explicitly dismiss ourselves just in case.
             dismiss();
         }
     };
@@ -139,4 +146,34 @@
         }
         super.dismiss();
     }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case com.android.internal.R.id.aerr_restart:
+                mHandler.obtainMessage(RESTART).sendToTarget();
+                break;
+            case com.android.internal.R.id.aerr_reset:
+                mHandler.obtainMessage(RESET).sendToTarget();
+                break;
+            case com.android.internal.R.id.aerr_report:
+                mHandler.obtainMessage(FORCE_QUIT_AND_REPORT).sendToTarget();
+                break;
+            case com.android.internal.R.id.aerr_close:
+                mHandler.obtainMessage(FORCE_QUIT).sendToTarget();
+                break;
+            case com.android.internal.R.id.aerr_mute:
+                mHandler.obtainMessage(MUTE).sendToTarget();
+                break;
+            default:
+                break;
+        }
+    }
+
+    static class Data {
+        AppErrorResult result;
+        TaskRecord task;
+        boolean repeating;
+        ProcessRecord proc;
+    }
 }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
new file mode 100644
index 0000000..58d9f45
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -0,0 +1,964 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import com.android.internal.app.ProcessMap;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.server.Watchdog;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
+import android.app.ApplicationErrorReport;
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.concurrent.Semaphore;
+
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE;
+
+/**
+ * Controls error conditions in applications.
+ */
+class AppErrors {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "AppErrors" : TAG_AM;
+
+    private final ActivityManagerService mService;
+    private final Context mContext;
+
+    private ArraySet<String> mAppsNotReportingCrashes;
+
+    /**
+     * The last time that various processes have crashed since they were last explicitly started.
+     */
+    private final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<>();
+
+    /**
+     * The last time that various processes have crashed (not reset even when explicitly started).
+     */
+    private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>();
+
+    /**
+     * Set of applications that we consider to be bad, and will reject
+     * incoming broadcasts from (which the user has no control over).
+     * Processes are added to this set when they have crashed twice within
+     * a minimum amount of time; they are removed from it when they are
+     * later restarted (hopefully due to some user action).  The value is the
+     * time it was added to the list.
+     */
+    private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>();
+
+
+    AppErrors(Context context, ActivityManagerService service) {
+        mService = service;
+        mContext = context;
+    }
+
+    boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep,
+            String dumpPackage) {
+        if (!mProcessCrashTimes.getMap().isEmpty()) {
+            boolean printed = false;
+            final long now = SystemClock.uptimeMillis();
+            final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+            final int processCount = pmap.size();
+            for (int ip = 0; ip < processCount; ip++) {
+                final String pname = pmap.keyAt(ip);
+                final SparseArray<Long> uids = pmap.valueAt(ip);
+                final int uidCount = uids.size();
+                for (int i = 0; i < uidCount; i++) {
+                    final int puid = uids.keyAt(i);
+                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    if (dumpPackage != null && (r == null
+                            || !r.pkgList.containsKey(dumpPackage))) {
+                        continue;
+                    }
+                    if (!printed) {
+                        if (needSep) pw.println();
+                        needSep = true;
+                        pw.println("  Time since processes crashed:");
+                        printed = true;
+                    }
+                    pw.print("    Process "); pw.print(pname);
+                    pw.print(" uid "); pw.print(puid);
+                    pw.print(": last crashed ");
+                    TimeUtils.formatDuration(now-uids.valueAt(i), pw);
+                    pw.println(" ago");
+                }
+            }
+        }
+
+        if (!mBadProcesses.getMap().isEmpty()) {
+            boolean printed = false;
+            final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
+            final int processCount = pmap.size();
+            for (int ip = 0; ip < processCount; ip++) {
+                final String pname = pmap.keyAt(ip);
+                final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
+                final int uidCount = uids.size();
+                for (int i = 0; i < uidCount; i++) {
+                    final int puid = uids.keyAt(i);
+                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    if (dumpPackage != null && (r == null
+                            || !r.pkgList.containsKey(dumpPackage))) {
+                        continue;
+                    }
+                    if (!printed) {
+                        if (needSep) pw.println();
+                        needSep = true;
+                        pw.println("  Bad processes:");
+                        printed = true;
+                    }
+                    final BadProcessInfo info = uids.valueAt(i);
+                    pw.print("    Bad process "); pw.print(pname);
+                    pw.print(" uid "); pw.print(puid);
+                    pw.print(": crashed at time "); pw.println(info.time);
+                    if (info.shortMsg != null) {
+                        pw.print("      Short msg: "); pw.println(info.shortMsg);
+                    }
+                    if (info.longMsg != null) {
+                        pw.print("      Long msg: "); pw.println(info.longMsg);
+                    }
+                    if (info.stack != null) {
+                        pw.println("      Stack:");
+                        int lastPos = 0;
+                        for (int pos = 0; pos < info.stack.length(); pos++) {
+                            if (info.stack.charAt(pos) == '\n') {
+                                pw.print("        ");
+                                pw.write(info.stack, lastPos, pos-lastPos);
+                                pw.println();
+                                lastPos = pos+1;
+                            }
+                        }
+                        if (lastPos < info.stack.length()) {
+                            pw.print("        ");
+                            pw.write(info.stack, lastPos, info.stack.length()-lastPos);
+                            pw.println();
+                        }
+                    }
+                }
+            }
+        }
+        return needSep;
+    }
+
+    boolean isBadProcessLocked(ApplicationInfo info) {
+        return mBadProcesses.get(info.processName, info.uid) != null;
+    }
+
+    void clearBadProcessLocked(ApplicationInfo info) {
+        mBadProcesses.remove(info.processName, info.uid);
+    }
+
+    void resetProcessCrashTimeLocked(ApplicationInfo info) {
+        mProcessCrashTimes.remove(info.processName, info.uid);
+    }
+
+    void resetProcessCrashTimeLocked(boolean resetEntireUser, int appId, int userId) {
+        final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+        for (int ip = pmap.size() - 1; ip >= 0; ip--) {
+            SparseArray<Long> ba = pmap.valueAt(ip);
+            for (int i = ba.size() - 1; i >= 0; i--) {
+                boolean remove = false;
+                final int entUid = ba.keyAt(i);
+                if (!resetEntireUser) {
+                    if (userId == UserHandle.USER_ALL) {
+                        if (UserHandle.getAppId(entUid) == appId) {
+                            remove = true;
+                        }
+                    } else {
+                        if (entUid == UserHandle.getUid(userId, appId)) {
+                            remove = true;
+                        }
+                    }
+                } else if (UserHandle.getUserId(entUid) == userId) {
+                    remove = true;
+                }
+                if (remove) {
+                    ba.removeAt(i);
+                }
+            }
+            if (ba.size() == 0) {
+                pmap.removeAt(ip);
+            }
+        }
+    }
+
+    void loadAppsNotReportingCrashesFromConfigLocked(String appsNotReportingCrashesConfig) {
+        if (appsNotReportingCrashesConfig != null) {
+            final String[] split = appsNotReportingCrashesConfig.split(",");
+            if (split.length > 0) {
+                mAppsNotReportingCrashes = new ArraySet<>();
+                Collections.addAll(mAppsNotReportingCrashes, split);
+            }
+        }
+    }
+
+    void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) {
+        app.crashing = false;
+        app.crashingReport = null;
+        app.notResponding = false;
+        app.notRespondingReport = null;
+        if (app.anrDialog == fromDialog) {
+            app.anrDialog = null;
+        }
+        if (app.waitDialog == fromDialog) {
+            app.waitDialog = null;
+        }
+        if (app.pid > 0 && app.pid != MY_PID) {
+            handleAppCrashLocked(app, "user-terminated" /*reason*/,
+                    null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
+            app.kill("user request after error", true);
+        }
+    }
+
+    void scheduleAppCrashLocked(int uid, int initialPid, String packageName,
+            String message) {
+        ProcessRecord proc = null;
+
+        // Figure out which process to kill.  We don't trust that initialPid
+        // still has any relation to current pids, so must scan through the
+        // list.
+
+        synchronized (mService.mPidsSelfLocked) {
+            for (int i=0; i<mService.mPidsSelfLocked.size(); i++) {
+                ProcessRecord p = mService.mPidsSelfLocked.valueAt(i);
+                if (p.uid != uid) {
+                    continue;
+                }
+                if (p.pid == initialPid) {
+                    proc = p;
+                    break;
+                }
+                if (p.pkgList.containsKey(packageName)) {
+                    proc = p;
+                }
+            }
+        }
+
+        if (proc == null) {
+            Slog.w(TAG, "crashApplication: nothing for uid=" + uid
+                    + " initialPid=" + initialPid
+                    + " packageName=" + packageName);
+            return;
+        }
+
+        if (proc.thread != null) {
+            if (proc.pid == Process.myPid()) {
+                Log.w(TAG, "crashApplication: trying to crash self!");
+                return;
+            }
+            long ident = Binder.clearCallingIdentity();
+            try {
+                proc.thread.scheduleCrash(message);
+            } catch (RemoteException e) {
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    /**
+     * Bring up the "unexpected error" dialog box for a crashing app.
+     * Deal with edge cases (intercepts from instrumented applications,
+     * ActivityController, error intent receivers, that sort of thing).
+     * @param r the application crashing
+     * @param crashInfo describing the failure
+     */
+    void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
+        long timeMillis = System.currentTimeMillis();
+        String shortMsg = crashInfo.exceptionClassName;
+        String longMsg = crashInfo.exceptionMessage;
+        String stackTrace = crashInfo.stackTrace;
+        if (shortMsg != null && longMsg != null) {
+            longMsg = shortMsg + ": " + longMsg;
+        } else if (shortMsg != null) {
+            longMsg = shortMsg;
+        }
+
+        AppErrorResult result = new AppErrorResult();
+        TaskRecord task;
+        synchronized (mService) {
+            if (mService.mController != null) {
+                try {
+                    String name = r != null ? r.processName : null;
+                    int pid = r != null ? r.pid : Binder.getCallingPid();
+                    int uid = r != null ? r.info.uid : Binder.getCallingUid();
+                    if (!mService.mController.appCrashed(name, pid,
+                            shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
+                        if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
+                                && "Native crash".equals(crashInfo.exceptionClassName)) {
+                            Slog.w(TAG, "Skip killing native crashed app " + name
+                                    + "(" + pid + ") during testing");
+                        } else {
+                            Slog.w(TAG, "Force-killing crashed app " + name
+                                    + " at watcher's request");
+                            if (r != null) {
+                                r.kill("crash", true);
+                            } else {
+                                // Huh.
+                                Process.killProcess(pid);
+                                ActivityManagerService.killProcessGroup(uid, pid);
+                            }
+                        }
+                        return;
+                    }
+                } catch (RemoteException e) {
+                    mService.mController = null;
+                    Watchdog.getInstance().setActivityController(null);
+                }
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+
+            // If this process is running instrumentation, finish it.
+            if (r != null && r.instrumentationClass != null) {
+                Slog.w(TAG, "Error in app " + r.processName
+                        + " running instrumentation " + r.instrumentationClass + ":");
+                if (shortMsg != null) Slog.w(TAG, "  " + shortMsg);
+                if (longMsg != null) Slog.w(TAG, "  " + longMsg);
+                Bundle info = new Bundle();
+                info.putString("shortMsg", shortMsg);
+                info.putString("longMsg", longMsg);
+                mService.finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info);
+                Binder.restoreCallingIdentity(origId);
+                return;
+            }
+
+            // Log crash in battery stats.
+            if (r != null) {
+                mService.mBatteryStatsService.noteProcessCrash(r.processName, r.uid);
+            }
+
+            AppErrorDialog.Data data = new AppErrorDialog.Data();
+            data.result = result;
+            data.proc = r;
+
+            // If we can't identify the process or it's already exceeded its crash quota,
+            // quit right away without showing a crash dialog.
+            if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
+                Binder.restoreCallingIdentity(origId);
+                return;
+            }
+
+            Message msg = Message.obtain();
+            msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
+
+            task = data.task;
+            msg.obj = data;
+            mService.mUiHandler.sendMessage(msg);
+
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        int res = result.get();
+
+        Intent appErrorIntent = null;
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (res == AppErrorDialog.RESET) {
+                String[] packageList = r.getPackageList();
+                if (packageList != null) {
+                    PackageManager pm = mContext.getPackageManager();
+                    final Semaphore s = new Semaphore(0);
+                    for (int i = 0; i < packageList.length; i++) {
+                        if (i < packageList.length - 1) {
+                            pm.deleteApplicationCacheFiles(packageList[i], null);
+                        } else {
+                            pm.deleteApplicationCacheFiles(packageList[i],
+                                    new IPackageDataObserver.Stub() {
+                                        @Override
+                                        public void onRemoveCompleted(String packageName,
+                                                boolean succeeded) {
+                                            s.release();
+                                        }
+                                    });
+
+                            // Wait until cache has been cleared before we restart.
+                            try {
+                                s.acquire();
+                            } catch (InterruptedException e) {
+                            }
+                        }
+                    }
+                }
+                // If there was nothing to reset, just restart;
+                res = AppErrorDialog.RESTART;
+            }
+            synchronized (mService) {
+                if (res == AppErrorDialog.MUTE) {
+                    stopReportingCrashesLocked(r);
+                }
+                if (res == AppErrorDialog.RESTART) {
+                    mService.removeProcessLocked(r, false, true, "crash");
+                    if (task != null) {
+                        try {
+                            mService.startActivityFromRecents(task.taskId,
+                                    ActivityOptions.makeBasic().toBundle());
+                        } catch (IllegalArgumentException e) {
+                            // Hmm, that didn't work, app might have crashed before creating a
+                            // recents entry. Let's see if we have a safe-to-restart intent.
+                            if (task.intent.getCategories().contains(
+                                    Intent.CATEGORY_LAUNCHER)) {
+                                mService.startActivityInPackage(task.mCallingUid,
+                                        task.mCallingPackage, task.intent,
+                                        null, null, null, 0, 0,
+                                        ActivityOptions.makeBasic().toBundle(),
+                                        task.userId, null, null);
+                            }
+                        }
+                    }
+                }
+                if (res == AppErrorDialog.FORCE_QUIT) {
+                    long orig = Binder.clearCallingIdentity();
+                    try {
+                        // Kill it with fire!
+                        mService.mStackSupervisor.handleAppCrashLocked(r);
+                        if (!r.persistent) {
+                            mService.removeProcessLocked(r, false, false, "crash");
+                            mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(orig);
+                    }
+                }
+                if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
+                    appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
+                }
+                if (r != null && !r.isolated && res != AppErrorDialog.RESTART) {
+                    // XXX Can't keep track of crash time for isolated processes,
+                    // since they don't have a persistent identity.
+                    mProcessCrashTimes.put(r.info.processName, r.uid,
+                            SystemClock.uptimeMillis());
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        if (appErrorIntent != null) {
+            try {
+                mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
+            } catch (ActivityNotFoundException e) {
+                Slog.w(TAG, "bug report receiver dissappeared", e);
+            }
+        }
+    }
+
+    private boolean makeAppCrashingLocked(ProcessRecord app,
+            String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
+        app.crashing = true;
+        app.crashingReport = generateProcessError(app,
+                ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
+        startAppProblemLocked(app);
+        app.stopFreezingAllLocked();
+        return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
+                data);
+    }
+
+    void startAppProblemLocked(ProcessRecord app) {
+        // If this app is not running under the current user, then we
+        // can't give it a report button because that would require
+        // launching the report UI under a different user.
+        app.errorReportReceiver = null;
+
+        for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) {
+            if (app.userId == userId) {
+                app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
+                        mContext, app.info.packageName, app.info.flags);
+            }
+        }
+        mService.skipCurrentReceiverLocked(app);
+    }
+
+    /**
+     * Generate a process error record, suitable for attachment to a ProcessRecord.
+     *
+     * @param app The ProcessRecord in which the error occurred.
+     * @param condition Crashing, Application Not Responding, etc.  Values are defined in
+     *                      ActivityManager.AppErrorStateInfo
+     * @param activity The activity associated with the crash, if known.
+     * @param shortMsg Short message describing the crash.
+     * @param longMsg Long message describing the crash.
+     * @param stackTrace Full crash stack trace, may be null.
+     *
+     * @return Returns a fully-formed AppErrorStateInfo record.
+     */
+    private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
+            int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
+        ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
+
+        report.condition = condition;
+        report.processName = app.processName;
+        report.pid = app.pid;
+        report.uid = app.info.uid;
+        report.tag = activity;
+        report.shortMsg = shortMsg;
+        report.longMsg = longMsg;
+        report.stackTrace = stackTrace;
+
+        return report;
+    }
+
+    Intent createAppErrorIntentLocked(ProcessRecord r,
+            long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
+        ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo);
+        if (report == null) {
+            return null;
+        }
+        Intent result = new Intent(Intent.ACTION_APP_ERROR);
+        result.setComponent(r.errorReportReceiver);
+        result.putExtra(Intent.EXTRA_BUG_REPORT, report);
+        result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return result;
+    }
+
+    private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r,
+            long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
+        if (r.errorReportReceiver == null) {
+            return null;
+        }
+
+        if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
+            return null;
+        }
+
+        ApplicationErrorReport report = new ApplicationErrorReport();
+        report.packageName = r.info.packageName;
+        report.installerPackageName = r.errorReportReceiver.getPackageName();
+        report.processName = r.processName;
+        report.time = timeMillis;
+        report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+
+        if (r.crashing || r.forceCrashReport) {
+            report.type = ApplicationErrorReport.TYPE_CRASH;
+            report.crashInfo = crashInfo;
+        } else if (r.notResponding) {
+            report.type = ApplicationErrorReport.TYPE_ANR;
+            report.anrInfo = new ApplicationErrorReport.AnrInfo();
+
+            report.anrInfo.activity = r.notRespondingReport.tag;
+            report.anrInfo.cause = r.notRespondingReport.shortMsg;
+            report.anrInfo.info = r.notRespondingReport.longMsg;
+        }
+
+        return report;
+    }
+
+    boolean handleAppCrashLocked(ProcessRecord app, String reason,
+            String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
+        long now = SystemClock.uptimeMillis();
+
+        Long crashTime;
+        Long crashTimePersistent;
+        if (!app.isolated) {
+            crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
+            crashTimePersistent = mProcessCrashTimesPersistent.get(app.info.processName, app.uid);
+        } else {
+            crashTime = crashTimePersistent = null;
+        }
+        if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
+            // This process loses!
+            Slog.w(TAG, "Process " + app.info.processName
+                    + " has crashed too many times: killing!");
+            EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
+                    app.userId, app.info.processName, app.uid);
+            mService.mStackSupervisor.handleAppCrashLocked(app);
+            if (!app.persistent) {
+                // We don't want to start this process again until the user
+                // explicitly does so...  but for persistent process, we really
+                // need to keep it running.  If a persistent process is actually
+                // repeatedly crashing, then badness for everyone.
+                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
+                        app.info.processName);
+                if (!app.isolated) {
+                    // XXX We don't have a way to mark isolated processes
+                    // as bad, since they don't have a peristent identity.
+                    mBadProcesses.put(app.info.processName, app.uid,
+                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
+                    mProcessCrashTimes.remove(app.info.processName, app.uid);
+                }
+                app.bad = true;
+                app.removed = true;
+                // Don't let services in this process be restarted and potentially
+                // annoy the user repeatedly.  Unless it is persistent, since those
+                // processes run critical code.
+                mService.removeProcessLocked(app, false, false, "crash");
+                mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                return false;
+            }
+            mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        } else {
+            TaskRecord affectedTask =
+                    mService.mStackSupervisor.finishTopRunningActivityLocked(app, reason);
+            if (data != null) {
+                data.task = affectedTask;
+            }
+            if (data != null && crashTimePersistent != null
+                    && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
+                data.repeating = true;
+            }
+        }
+
+        // Bump up the crash count of any services currently running in the proc.
+        for (int i=app.services.size()-1; i>=0; i--) {
+            // Any services running in the application need to be placed
+            // back in the pending list.
+            ServiceRecord sr = app.services.valueAt(i);
+            sr.crashCount++;
+        }
+
+        // If the crashing process is what we consider to be the "home process" and it has been
+        // replaced by a third-party app, clear the package preferred activities from packages
+        // with a home activity running in the process to prevent a repeatedly crashing app
+        // from blocking the user to manually clear the list.
+        final ArrayList<ActivityRecord> activities = app.activities;
+        if (app == mService.mHomeProcess && activities.size() > 0
+                && (mService.mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.isHomeActivity()) {
+                    Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
+                    try {
+                        ActivityThread.getPackageManager()
+                                .clearPackagePreferredActivities(r.packageName);
+                    } catch (RemoteException c) {
+                        // pm is in same process, this will never happen.
+                    }
+                }
+            }
+        }
+
+        if (!app.isolated) {
+            // XXX Can't keep track of crash times for isolated processes,
+            // because they don't have a perisistent identity.
+            mProcessCrashTimes.put(app.info.processName, app.uid, now);
+            mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now);
+        }
+
+        if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
+        return true;
+    }
+
+    void handleShowAppErrorUi(Message msg) {
+        AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;
+        boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+        synchronized (mService) {
+            ProcessRecord proc = data.proc;
+            AppErrorResult res = data.result;
+            if (proc != null && proc.crashDialog != null) {
+                Slog.e(TAG, "App already has crash dialog: " + proc);
+                if (res != null) {
+                    res.set(0);
+                }
+                return;
+            }
+            boolean isBackground = (UserHandle.getAppId(proc.uid)
+                    >= Process.FIRST_APPLICATION_UID
+                    && proc.pid != MY_PID);
+            for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) {
+                isBackground &= (proc.userId != userId);
+            }
+            if (isBackground && !showBackground) {
+                Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
+                if (res != null) {
+                    res.set(0);
+                }
+                return;
+            }
+            final boolean crashSilenced = mAppsNotReportingCrashes != null &&
+                    mAppsNotReportingCrashes.contains(proc.info.packageName);
+            if (mService.canShowErrorDialogs() && !crashSilenced) {
+                Dialog d = new AppErrorDialog(mContext, mService, data);
+                d.show();
+                proc.crashDialog = d;
+            } else {
+                // The device is asleep, so just pretend that the user
+                // saw a crash dialog and hit "force quit".
+                if (res != null) {
+                    res.set(0);
+                }
+            }
+        }
+    }
+
+    void stopReportingCrashesLocked(ProcessRecord proc) {
+        if (mAppsNotReportingCrashes == null) {
+            mAppsNotReportingCrashes = new ArraySet<>();
+        }
+        mAppsNotReportingCrashes.add(proc.info.packageName);
+    }
+
+    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
+            ActivityRecord parent, boolean aboveSystem, final String annotation) {
+        ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
+        SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
+
+        if (mService.mController != null) {
+            try {
+                // 0 == continue, -1 = kill process immediately
+                int res = mService.mController.appEarlyNotResponding(app.processName, app.pid, annotation);
+                if (res < 0 && app.pid != MY_PID) {
+                    app.kill("anr", true);
+                }
+            } catch (RemoteException e) {
+                mService.mController = null;
+                Watchdog.getInstance().setActivityController(null);
+            }
+        }
+
+        long anrTime = SystemClock.uptimeMillis();
+        if (ActivityManagerService.MONITOR_CPU_USAGE) {
+            mService.updateCpuStatsNow();
+        }
+
+        synchronized (mService) {
+            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
+            if (mService.mShuttingDown) {
+                Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
+                return;
+            } else if (app.notResponding) {
+                Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
+                return;
+            } else if (app.crashing) {
+                Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
+                return;
+            }
+
+            // In case we come through here for the same app before completing
+            // this one, mark as anring now so we will bail out.
+            app.notResponding = true;
+
+            // Log the ANR to the event log.
+            EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
+                    app.processName, app.info.flags, annotation);
+
+            // Dump thread traces as quickly as we can, starting with "interesting" processes.
+            firstPids.add(app.pid);
+
+            int parentPid = app.pid;
+            if (parent != null && parent.app != null && parent.app.pid > 0) {
+                parentPid = parent.app.pid;
+            }
+            if (parentPid != app.pid) firstPids.add(parentPid);
+
+            if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
+
+            for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
+                ProcessRecord r = mService.mLruProcesses.get(i);
+                if (r != null && r.thread != null) {
+                    int pid = r.pid;
+                    if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
+                        if (r.persistent) {
+                            firstPids.add(pid);
+                        } else {
+                            lastPids.put(pid, Boolean.TRUE);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Log the ANR to the main log.
+        StringBuilder info = new StringBuilder();
+        info.setLength(0);
+        info.append("ANR in ").append(app.processName);
+        if (activity != null && activity.shortComponentName != null) {
+            info.append(" (").append(activity.shortComponentName).append(")");
+        }
+        info.append("\n");
+        info.append("PID: ").append(app.pid).append("\n");
+        if (annotation != null) {
+            info.append("Reason: ").append(annotation).append("\n");
+        }
+        if (parent != null && parent != activity) {
+            info.append("Parent: ").append(parent.shortComponentName).append("\n");
+        }
+
+        final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
+
+        File tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
+                NATIVE_STACKS_OF_INTEREST);
+
+        String cpuInfo = null;
+        if (ActivityManagerService.MONITOR_CPU_USAGE) {
+            mService.updateCpuStatsNow();
+            synchronized (mService.mProcessCpuTracker) {
+                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
+            }
+            info.append(processCpuTracker.printCurrentLoad());
+            info.append(cpuInfo);
+        }
+
+        info.append(processCpuTracker.printCurrentState(anrTime));
+
+        Slog.e(TAG, info.toString());
+        if (tracesFile == null) {
+            // There is no trace file, so dump (only) the alleged culprit's threads to the log
+            Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
+        }
+
+        mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
+                cpuInfo, tracesFile, null);
+
+        if (mService.mController != null) {
+            try {
+                // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
+                int res = mService.mController.appNotResponding(
+                        app.processName, app.pid, info.toString());
+                if (res != 0) {
+                    if (res < 0 && app.pid != MY_PID) {
+                        app.kill("anr", true);
+                    } else {
+                        synchronized (mService) {
+                            mService.mServices.scheduleServiceTimeoutLocked(app);
+                        }
+                    }
+                    return;
+                }
+            } catch (RemoteException e) {
+                mService.mController = null;
+                Watchdog.getInstance().setActivityController(null);
+            }
+        }
+
+        // Unless configured otherwise, swallow ANRs in background processes & kill the process.
+        boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+
+        synchronized (mService) {
+            mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
+
+            if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
+                app.kill("bg anr", true);
+                return;
+            }
+
+            // Set the app's notResponding state, and look up the errorReportReceiver
+            makeAppNotRespondingLocked(app,
+                    activity != null ? activity.shortComponentName : null,
+                    annotation != null ? "ANR " + annotation : "ANR",
+                    info.toString());
+
+            // Bring up the infamous App Not Responding dialog
+            Message msg = Message.obtain();
+            HashMap<String, Object> map = new HashMap<String, Object>();
+            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
+            msg.obj = map;
+            msg.arg1 = aboveSystem ? 1 : 0;
+            map.put("app", app);
+            if (activity != null) {
+                map.put("activity", activity);
+            }
+
+            mService.mUiHandler.sendMessage(msg);
+        }
+    }
+
+    private void makeAppNotRespondingLocked(ProcessRecord app,
+            String activity, String shortMsg, String longMsg) {
+        app.notResponding = true;
+        app.notRespondingReport = generateProcessError(app,
+                ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
+                activity, shortMsg, longMsg, null);
+        startAppProblemLocked(app);
+        app.stopFreezingAllLocked();
+    }
+
+    void handleShowAnrUi(Message msg) {
+        synchronized (mService) {
+            HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
+            ProcessRecord proc = (ProcessRecord)data.get("app");
+            if (proc != null && proc.anrDialog != null) {
+                Slog.e(TAG, "App already has anr dialog: " + proc);
+                return;
+            }
+
+            Intent intent = new Intent("android.intent.action.ANR");
+            if (!mService.mProcessesReady) {
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                        | Intent.FLAG_RECEIVER_FOREGROUND);
+            }
+            mService.broadcastIntentLocked(null, null, intent,
+                    null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+                    null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+
+            if (mService.canShowErrorDialogs()) {
+                Dialog d = new AppNotRespondingDialog(mService,
+                        mContext, proc, (ActivityRecord)data.get("activity"),
+                        msg.arg1 != 0);
+                d.show();
+                proc.anrDialog = d;
+            } else {
+                // Just kill the app if there is no dialog to be shown.
+                mService.killAppAtUsersRequest(proc, null);
+            }
+        }
+    }
+
+    /**
+     * Information about a process that is currently marked as bad.
+     */
+    static final class BadProcessInfo {
+        BadProcessInfo(long time, String shortMsg, String longMsg, String stack) {
+            this.time = time;
+            this.shortMsg = shortMsg;
+            this.longMsg = longMsg;
+            this.stack = stack;
+        }
+
+        final long time;
+        final String shortMsg;
+        final String longMsg;
+        final String stack;
+    }
+
+}
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index f4c1664..4587b72 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -117,7 +117,7 @@
                         ProcessRecord app = mProc;
 
                         if (msg.what == WAIT_AND_REPORT) {
-                            appErrorIntent = mService.createAppErrorIntentLocked(app,
+                            appErrorIntent = mService.mAppErrors.createAppErrorIntentLocked(app,
                                     System.currentTimeMillis(), null);
                         }
 
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 622aa16..37b0af1 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -199,7 +199,7 @@
 
         @Override
         public void run() {
-            mService.appNotResponding(mApp, null, null, false, mAnnotation);
+            mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 3f0674d..9c139d5 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -24,6 +24,8 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
+import com.google.android.collect.Sets;
+
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.content.ComponentName;
@@ -45,6 +47,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.Set;
 
 /**
  * Class for managing the recent tasks list.
@@ -188,6 +191,21 @@
         }
     }
 
+    void onPackagesSuspendedChanged(String[] packages, boolean suspended, int userId) {
+        final Set<String> packageNames = Sets.newHashSet(packages);
+        for (int i = size() - 1; i >= 0; --i) {
+            final TaskRecord tr = get(i);
+            if (tr.realActivity != null
+                    && packageNames.contains(tr.realActivity.getPackageName())
+                    && tr.userId == userId
+                    && tr.realActivitySuspended != suspended) {
+               tr.realActivitySuspended = suspended;
+               notifyTaskPersisterLocked(tr, false);
+            }
+        }
+
+    }
+
     /**
      * Update the recent tasks lists: make sure tasks should still be here (their
      * applications / activities still exist), update their availability, fix-up ordering
@@ -683,5 +701,4 @@
         // Let the caller know where we left off.
         return start + tmpSize;
     }
-
 }
diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
index fda1ec1..6da84bd 100644
--- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java
@@ -73,7 +73,6 @@
                       mHandler.obtainMessage(ACTION_OK_AND_REPORT));
         }
 
-        setTitle(res.getText(com.android.internal.R.string.aerr_title));
         getWindow().addPrivateFlags(PRIVATE_FLAG_SYSTEM_ERROR);
         getWindow().setTitle("Strict Mode Violation: " + app.info.processName);
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4ce8b2f..fd787df 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -95,6 +95,7 @@
     private static final String TAG_INTENT = "intent";
     private static final String TAG_AFFINITYINTENT = "affinity_intent";
     static final String ATTR_REALACTIVITY = "real_activity";
+    static final String ATTR_REALACTIVITY_SUSPENDED = "real_activity_suspended";
     private static final String ATTR_ORIGACTIVITY = "orig_activity";
     private static final String TAG_ACTIVITY = "activity";
     private static final String ATTR_AFFINITY = "affinity";
@@ -136,6 +137,8 @@
     int effectiveUid;       // The current effective uid of the identity of this task.
     ComponentName origActivity; // The non-alias activity component of the intent.
     ComponentName realActivity; // The actual activity component that started the task.
+    boolean realActivitySuspended; // True if the actual activity component that started the
+                                   // task is suspended.
     long firstActiveTime;   // First time this task was active.
     long lastActiveTime;    // Last time this task was active, including sleep.
     boolean inRecents;      // Actually in the recents list?
@@ -305,7 +308,7 @@
             boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
             TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
             int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-            boolean resizeable, boolean privileged) {
+            boolean resizeable, boolean privileged, boolean realActivitySuspended) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
@@ -319,6 +322,7 @@
         voiceSession = null;
         voiceInteractor = null;
         realActivity = _realActivity;
+        realActivitySuspended = realActivitySuspended;
         origActivity = _origActivity;
         rootWasReset = _rootWasReset;
         isAvailable = true;
@@ -1027,6 +1031,7 @@
         if (realActivity != null) {
             out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
         }
+        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
         if (origActivity != null) {
             out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
         }
@@ -1105,6 +1110,7 @@
         Intent affinityIntent = null;
         ArrayList<ActivityRecord> activities = new ArrayList<>();
         ComponentName realActivity = null;
+        boolean realActivitySuspended = false;
         ComponentName origActivity = null;
         String affinity = null;
         String rootAffinity = null;
@@ -1143,6 +1149,8 @@
                 if (taskId == INVALID_TASK_ID) taskId = Integer.valueOf(attrValue);
             } else if (ATTR_REALACTIVITY.equals(attrName)) {
                 realActivity = ComponentName.unflattenFromString(attrValue);
+            } else if (ATTR_REALACTIVITY_SUSPENDED.equals(attrName)) {
+                realActivitySuspended = Boolean.valueOf(attrValue);
             } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
                 origActivity = ComponentName.unflattenFromString(attrValue);
             } else if (ATTR_AFFINITY.equals(attrName)) {
@@ -1253,7 +1261,8 @@
                 autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
                 taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
-                taskAffiliationColor, callingUid, callingPackage, resizeable, privileged);
+                taskAffiliationColor, callingUid, callingPackage, resizeable, privileged,
+                realActivitySuspended);
         task.updateOverrideConfiguration(bounds);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3b43633..6648efd 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -19,6 +19,10 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -38,20 +42,25 @@
 import android.net.NetworkRequest;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.wifi.WifiManager;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.IState;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.IoThread;
@@ -59,8 +68,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.net.InetAddress;
 import java.net.Inet4Address;
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -109,6 +118,10 @@
 
     private BroadcastReceiver mStateReceiver;
 
+    // {@link ComponentName} of the Service used to run tether provisioning.
+    private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
+            .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
+
     private static final String USB_NEAR_IFACE_ADDR      = "192.168.42.129";
     private static final int USB_PREFIX_LENGTH        = 24;
 
@@ -332,6 +345,218 @@
         }
     }
 
+    public void startTethering(int type, ResultReceiver receiver,
+            boolean showProvisioningUi) {
+        if (!isTetherProvisioningRequired()) {
+            enableTetheringInternal(type, true, receiver);
+            return;
+        }
+
+        if (showProvisioningUi) {
+            runUiTetherProvisioningAndEnable(type, receiver);
+        } else {
+            runSilentTetherProvisioningAndEnable(type, receiver);
+        }
+    }
+
+    public void stopTethering(int type) {
+        enableTetheringInternal(type, false, null);
+        if (isTetherProvisioningRequired()) {
+            cancelTetherProvisioningRechecks(type);
+        }
+    }
+
+    /**
+     * Check if the device requires a provisioning check in order to enable tethering.
+     *
+     * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
+     */
+    private boolean isTetherProvisioningRequired() {
+        String[] provisionApp = mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_mobile_hotspot_provision_app);
+        if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)
+                || provisionApp == null) {
+            return false;
+        }
+
+        // Check carrier config for entitlement checks
+        final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+             .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        boolean isEntitlementCheckRequired = configManager.getConfig().getBoolean(
+             CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+
+        if (!isEntitlementCheckRequired) {
+            return false;
+        }
+        return (provisionApp.length == 2);
+    }
+
+    /**
+     * Enables or disables tethering for the given type. This should only be called once
+     * provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
+     * for the specified interface.
+     */
+    private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
+        boolean isProvisioningRequired = isTetherProvisioningRequired();
+        switch (type) {
+            case ConnectivityManager.TETHERING_WIFI:
+                final WifiManager wifiManager =
+                        (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+                if (wifiManager.setWifiApEnabled(null, enable)) {
+                    sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_NO_ERROR);
+                    if (enable && isProvisioningRequired) {
+                        scheduleProvisioningRechecks(type);
+                    }
+                } else{
+                    sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
+                }
+                break;
+            case ConnectivityManager.TETHERING_USB:
+                int result = setUsbTethering(enable);
+                if (enable && isProvisioningRequired &&
+                        result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                    scheduleProvisioningRechecks(type);
+                }
+                sendTetherResult(receiver, result);
+                break;
+            case ConnectivityManager.TETHERING_BLUETOOTH:
+                setBluetoothTethering(enable, receiver);
+                break;
+            default:
+                Log.w(TAG, "Invalid tether type.");
+                sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE);
+        }
+    }
+
+    private void sendTetherResult(ResultReceiver receiver, int result) {
+        if (receiver != null) {
+            receiver.send(result, null);
+        }
+    }
+
+    private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
+        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter == null || !adapter.isEnabled()) {
+            Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " +
+                    (adapter == null));
+            sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL);
+            return;
+        }
+
+        adapter.getProfileProxy(mContext, new ServiceListener() {
+            @Override
+            public void onServiceDisconnected(int profile) { }
+
+            @Override
+            public void onServiceConnected(int profile, BluetoothProfile proxy) {
+                ((BluetoothPan) proxy).setBluetoothTethering(enable);
+                // TODO: Enabling bluetooth tethering can fail asynchronously here.
+                // We should figure out a way to bubble up that failure instead of sending success.
+                int result = ((BluetoothPan) proxy).isTetheringOn() ?
+                        ConnectivityManager.TETHER_ERROR_NO_ERROR :
+                        ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+                sendTetherResult(receiver, result);
+                if (enable && isTetherProvisioningRequired()) {
+                    scheduleProvisioningRechecks(ConnectivityManager.TETHERING_BLUETOOTH);
+                }
+                adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
+            }
+        }, BluetoothProfile.PAN);
+    }
+
+    private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+        ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
+        sendUiTetherProvisionIntent(type, proxyReceiver);
+    }
+
+    private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
+        Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
+        intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Creates a proxy {@link ResultReceiver} which enables tethering if the provsioning result is
+     * successful before firing back up to the wrapped receiver.
+     *
+     * @param type The type of tethering being enabled.
+     * @param receiver A ResultReceiver which will be called back with an int resultCode.
+     * @return The proxy receiver.
+     */
+    private ResultReceiver getProxyReceiver(final int type, final ResultReceiver receiver) {
+        ResultReceiver rr = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                // If provisioning is successful, enable tethering, otherwise just send the error.
+                if (resultCode == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                    enableTetheringInternal(type, true, receiver);
+                } else {
+                    sendTetherResult(receiver, resultCode);
+                }
+            }
+        };
+
+        // The following is necessary to avoid unmarshalling issues when sending the receiver
+        // across proccesses.
+        Parcel parcel = Parcel.obtain();
+        rr.writeToParcel(parcel,0);
+        parcel.setDataPosition(0);
+        ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return receiverForSending;
+    }
+
+    private void scheduleProvisioningRechecks(int type) {
+        Intent intent = new Intent();
+        intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(ConnectivityManager.EXTRA_SET_ALARM, true);
+        intent.setComponent(TETHER_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+        ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
+        sendSilentTetherProvisionIntent(type, proxyReceiver);
+    }
+
+    private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
+        Intent intent = new Intent();
+        intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
+        intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+        intent.setComponent(TETHER_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void cancelTetherProvisioningRechecks(int type) {
+        if (getConnectivityManager().isTetheringSupported()) {
+            Intent intent = new Intent();
+            intent.putExtra(ConnectivityManager.EXTRA_REM_TETHER_TYPE, type);
+            intent.setComponent(TETHER_SERVICE);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
     public int tether(String iface) {
         if (DBG) Log.d(TAG, "Tethering " + iface);
         TetherInterfaceSM sm = null;
@@ -615,13 +840,23 @@
         synchronized (mPublicSync) {
             if (enable) {
                 if (mRndisEnabled) {
-                    tetherUsb(true);
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        tetherUsb(true);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
                 } else {
                     mUsbTetherRequested = true;
                     usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS);
                 }
             } else {
-                tetherUsb(false);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    tetherUsb(false);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
                 if (mRndisEnabled) {
                     usbManager.setCurrentFunction(null);
                 }
@@ -1407,15 +1642,6 @@
         private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
         private SimChangeBroadcastReceiver mBroadcastReceiver = null;
 
-        // keep consts in sync with packages/apps/Settings TetherSettings.java
-        private static final int WIFI_TETHERING      = 0;
-        private static final int USB_TETHERING       = 1;
-        private static final int BLUETOOTH_TETHERING = 2;
-
-        // keep consts in sync with packages/apps/Settings TetherService.java
-        private static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
-        private static final String EXTRA_RUN_PROVISION = "extraRunProvision";
-
         private void startListeningForSimChanges() {
             if (DBG) Log.d(TAG, "startListeningForSimChanges");
             if (mBroadcastReceiver == null) {
@@ -1472,8 +1698,6 @@
                     try {
                         if (mContext.getResources().getString(com.android.internal.R.string.
                                 config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
-                            final String tetherService = mContext.getResources().getString(
-                                    com.android.internal.R.string.config_wifi_tether_enable);
                             ArrayList<Integer> tethered = new ArrayList<Integer>();
                             synchronized (mPublicSync) {
                                 Set ifaces = mIfaces.keySet();
@@ -1481,21 +1705,25 @@
                                     TetherInterfaceSM sm = mIfaces.get(iface);
                                     if (sm != null && sm.isTethered()) {
                                         if (isUsb((String)iface)) {
-                                            tethered.add(new Integer(USB_TETHERING));
+                                            tethered.add(new Integer(
+                                                    ConnectivityManager.TETHERING_USB));
                                         } else if (isWifi((String)iface)) {
-                                            tethered.add(new Integer(WIFI_TETHERING));
+                                            tethered.add(new Integer(
+                                                    ConnectivityManager.TETHERING_WIFI));
                                         } else if (isBluetooth((String)iface)) {
-                                            tethered.add(new Integer(BLUETOOTH_TETHERING));
+                                            tethered.add(new Integer(
+                                                    ConnectivityManager.TETHERING_BLUETOOTH));
                                         }
                                     }
                                 }
                             }
                             for (int tetherType : tethered) {
                                 Intent startProvIntent = new Intent();
-                                startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
-                                startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
-                                startProvIntent.setComponent(
-                                        ComponentName.unflattenFromString(tetherService));
+                                startProvIntent.putExtra(
+                                        ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
+                                startProvIntent.putExtra(
+                                        ConnectivityManager.EXTRA_RUN_PROVISION, true);
+                                startProvIntent.setComponent(TETHER_SERVICE);
                                 mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
                             }
                             Log.d(TAG, "re-evaluate provisioning");
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 7ac3c4b..e74d636 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.trust.TrustManager;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
@@ -103,6 +104,7 @@
     private static final int MAX_FAILED_ATTEMPTS = 5;
     private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
     private final String mKeyguardPackage;
+    private int mCurrentUserId = UserHandle.USER_CURRENT;
 
     Handler mHandler = new Handler() {
         @Override
@@ -125,6 +127,7 @@
     private IFingerprintDaemon mDaemon;
     private final PowerManager mPowerManager;
     private final AlarmManager mAlarmManager;
+    private final UserManager mUserManager;
 
     private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
         @Override
@@ -152,6 +155,7 @@
         mAlarmManager = mContext.getSystemService(AlarmManager.class);
         mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
                 RESET_FINGERPRINT_LOCKOUT, null /* handler */);
+        mUserManager = UserManager.get(mContext);
     }
 
     @Override
@@ -170,7 +174,7 @@
                     mDaemon.init(mDaemonCallback);
                     mHalDeviceId = mDaemon.openHal();
                     if (mHalDeviceId != 0) {
-                        updateActiveGroup(ActivityManager.getCurrentUser());
+                        updateActiveGroup(ActivityManager.getCurrentUser(), null);
                     } else {
                         Slog.w(TAG, "Failed to open Fingerprint HAL!");
                         mDaemon = null;
@@ -261,7 +265,7 @@
     }
 
     void handleUserSwitching(int userId) {
-        updateActiveGroup(userId);
+        updateActiveGroup(userId, null);
     }
 
     private void removeClient(ClientMonitor client) {
@@ -414,7 +418,7 @@
         removeClient(mEnrollClient);
     }
 
-    void startAuthentication(IBinder token, long opId, int groupId,
+    void startAuthentication(IBinder token, long opId, int realUserId, int groupId,
             IFingerprintServiceReceiver receiver, int flags, boolean restricted,
             String opPackageName) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
@@ -423,6 +427,7 @@
             return;
         }
         stopPendingOperations(true);
+        updateActiveGroup(groupId, opPackageName);
         mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName);
         if (inLockoutMode()) {
             Slog.v(TAG, "In lockout mode; disallowing authentication");
@@ -564,7 +569,7 @@
         checkPermission(USE_FINGERPRINT);
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
-        if (opPackageName.equals(mKeyguardPackage)) {
+        if (isKeyguard(opPackageName)) {
             return true; // Keyguard is always allowed
         }
         if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
@@ -583,6 +588,14 @@
         return true;
     }
 
+    /**
+     * @param clientPackage
+     * @return true if this is keyguard package
+     */
+    private boolean isKeyguard(String clientPackage) {
+        return mKeyguardPackage.equals(clientPackage);
+    }
+
     private void addLockoutResetMonitor(FingerprintServiceLockoutResetMonitor monitor) {
         if (!mLockoutMonitors.contains(monitor)) {
             mLockoutMonitors.add(monitor);
@@ -927,14 +940,15 @@
             // Group ID is arbitrarily set to parent profile user ID. It just represents
             // the default fingerprints for the user.
             final int effectiveGroupId = getEffectiveUserId(groupId);
+            final int realUserId = Binder.getCallingUid();
 
             final boolean restricted = isRestricted();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
-                    startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted,
-                            opPackageName);
+                    startAuthentication(token, opId, realUserId, effectiveGroupId, receiver,
+                            flags, restricted, opPackageName);
                 }
             });
         }
@@ -953,6 +967,17 @@
         }
 
         @Override // Binder call
+        public void setActiveUser(final int userId) {
+            checkPermission(MANAGE_FINGERPRINT);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    updateActiveGroup(userId, null);
+                }
+            });
+        }
+
+        @Override // Binder call
         public void remove(final IBinder token, final int fingerId, final int groupId,
                 final IFingerprintServiceReceiver receiver) {
             checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
@@ -1102,33 +1127,56 @@
         listenForUserSwitches();
     }
 
-    private void updateActiveGroup(int userId) {
+    private void updateActiveGroup(int userId, String clientPackage) {
         IFingerprintDaemon daemon = getFingerprintDaemon();
         if (daemon != null) {
             try {
-                userId = getEffectiveUserId(userId);
-                final File systemDir = Environment.getUserSystemDirectory(userId);
-                final File fpDir = new File(systemDir, FP_DATA_DIR);
-                if (!fpDir.exists()) {
-                    if (!fpDir.mkdir()) {
-                        Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
-                        return;
+                userId = getUserOrWorkProfileId(clientPackage, userId);
+                if (userId != mCurrentUserId) {
+                    final File systemDir = Environment.getUserSystemDirectory(userId);
+                    final File fpDir = new File(systemDir, FP_DATA_DIR);
+                    if (!fpDir.exists()) {
+                        if (!fpDir.mkdir()) {
+                            Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
+                            return;
+                        }
+                        // Calling mkdir() from this process will create a directory with our
+                        // permissions (inherited from the containing dir). This command fixes
+                        // the label.
+                        if (!SELinux.restorecon(fpDir)) {
+                            Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+                            return;
+                        }
                     }
-                    // Calling mkdir() from this process will create a directory with our
-                    // permissions (inherited from the containing dir). This command fixes
-                    // the label.
-                    if (!SELinux.restorecon(fpDir)) {
-                        Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
-                        return;
-                    }
+                    daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
+                    mCurrentUserId = userId;
                 }
-                daemon.setActiveGroup(userId, fpDir.getAbsolutePath().getBytes());
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to setActiveGroup():", e);
             }
         }
     }
 
+    /**
+     * @param clientPackage the package of the caller
+     * @return the profile id
+     */
+    private int getUserOrWorkProfileId(String clientPackage, int userId) {
+        if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
+            return userId;
+        }
+        return getEffectiveUserId(userId);
+    }
+
+    /**
+     * @param userId
+     * @return true if this is a work profile
+     */
+    private boolean isWorkProfile(int userId) {
+        UserInfo info = mUserManager.getUserInfo(userId);
+        return info != null && info.isManagedProfile();
+    }
+
     private void listenForUserSwitches() {
         try {
             ActivityManagerNative.getDefault().registerUserSwitchObserver(
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 1d7d24b..492632c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1155,7 +1155,7 @@
         final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length);
         final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
         for (NetworkState state : states) {
-            if (state.networkInfo.isConnected()) {
+            if (state.networkInfo != null && state.networkInfo.isConnected()) {
                 final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
 
                 final String baseIface = state.linkProperties.getInterfaceName();
@@ -1990,6 +1990,10 @@
         if (policy != null) {
             return policy.metered;
         } else {
+            if (state.networkInfo == null) {
+                return false;
+            }
+
             final int type = state.networkInfo.getType();
             if (isNetworkTypeMobile(type) || type == TYPE_WIMAX) {
                 return true;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2ee74db..7f783ec 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1242,58 +1242,55 @@
         }
 
         @Override
-        public void setTopicPriority(String pkg, int uid, Notification.Topic topic, int priority) {
+        public void setPriority(String pkg, int uid, Notification.Topic topic, int priority) {
             checkCallerIsSystem();
-            mRankingHelper.setTopicPriority(pkg, uid, topic, priority);
+            mRankingHelper.setPriority(pkg, uid, topic, priority);
             savePolicyFile();
         }
 
         @Override
-        public int getTopicPriority(String pkg, int uid, Notification.Topic topic) {
+        public int getPriority(String pkg, int uid, Notification.Topic topic) {
             checkCallerIsSystem();
-            return mRankingHelper.getTopicPriority(pkg, uid, topic);
+            return mRankingHelper.getPriority(pkg, uid, topic);
         }
 
         @Override
-        public void setTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic,
+        public void setVisibilityOverride(String pkg, int uid, Notification.Topic topic,
                 int visibility) {
             checkCallerIsSystem();
-            mRankingHelper.setTopicVisibilityOverride(pkg, uid, topic, visibility);
+            mRankingHelper.setVisibilityOverride(pkg, uid, topic, visibility);
             savePolicyFile();
         }
 
         @Override
-        public int getTopicVisibilityOverride(String pkg, int uid, Notification.Topic topic) {
+        public int getVisibilityOverride(String pkg, int uid, Notification.Topic topic) {
             checkCallerIsSystem();
-            return mRankingHelper.getTopicVisibilityOverride(pkg, uid, topic);
+            return mRankingHelper.getVisibilityOverride(pkg, uid, topic);
         }
 
         @Override
-        public void setTopicImportance(String pkg, int uid, Notification.Topic topic,
+        public void setImportance(String pkg, int uid, Notification.Topic topic,
                 int importance) {
             enforceSystemOrSystemUI("Caller not system or systemui");
-            if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) {
-                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true,
-                        UserHandle.getUserId(uid),
-                        REASON_TOPIC_BANNED, topic, null);
+            if (topic == null) {
+                // App wide, potentially store block in app ops.
+                setNotificationsEnabledForPackageImpl(pkg, uid,
+                        importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
+            } else {
+                if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) {
+                    cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true,
+                            UserHandle.getUserId(uid),
+                            REASON_TOPIC_BANNED, topic, null);
+                }
             }
-            mRankingHelper.setTopicImportance(pkg, uid, topic, importance);
+            mRankingHelper.setImportance(pkg, uid, topic, importance);
             savePolicyFile();
         }
 
         @Override
-        public int getTopicImportance(String pkg, int uid, Notification.Topic topic) {
+        public int getImportance(String pkg, int uid, Notification.Topic topic) {
             checkCallerIsSystem();
-            return mRankingHelper.getTopicImportance(pkg, uid, topic);
-        }
-
-        @Override
-        public void setAppImportance(String pkg, int uid, int importance) {
-            enforceSystemOrSystemUI("Caller not system or systemui");
-            setNotificationsEnabledForPackageImpl(pkg, uid,
-                    importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
-            mRankingHelper.setAppImportance(pkg, uid, importance);
-            savePolicyFile();
+            return mRankingHelper.getImportance(pkg, uid, topic);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 490e890..484b0e9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -33,6 +33,7 @@
 import android.service.notification.StatusBarNotification;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -106,6 +107,7 @@
         mCreationTimeMs = sbn.getPostTime();
         mUpdateTimeMs = mCreationTimeMs;
         mContext = context;
+        stats = new NotificationUsageStats.SingleNotificationStats();
         mImportance = defaultImportance();
     }
 
@@ -133,27 +135,22 @@
                 importance = IMPORTANCE_MAX;
                 break;
         }
+        stats.requestedImportance = importance;
 
         boolean isNoisy = (n.defaults & Notification.DEFAULT_SOUND) != 0
                 || (n.defaults & Notification.DEFAULT_VIBRATE) != 0
                 || n.sound != null
                 || n.vibrate != null;
+        stats.isNoisy = isNoisy;
         if (!isNoisy && importance > IMPORTANCE_DEFAULT) {
             importance = IMPORTANCE_DEFAULT;
         }
-        // maybe only do this for target API < N?
-        if (isNoisy) {
-            if (importance >= IMPORTANCE_HIGH) {
-                importance = IMPORTANCE_MAX;
-            } else {
-                importance = IMPORTANCE_HIGH;
-            }
-        }
 
         if (n.fullScreenIntent != null) {
             importance = IMPORTANCE_MAX;
         }
 
+        stats.naturalImportance = importance;
         return importance;
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 1cdc6db..e75324f 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
+
 import android.app.Notification;
 import android.content.ContentValues;
 import android.content.Context;
@@ -101,7 +103,6 @@
      * Called when a notification has been posted.
      */
     public synchronized void registerPostedByApp(NotificationRecord notification) {
-        notification.stats = new SingleNotificationStats();
         notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime();
 
         AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
@@ -119,13 +120,16 @@
      * Called when a notification has been updated.
      */
     public void registerUpdatedByApp(NotificationRecord notification, NotificationRecord old) {
-        notification.stats = old.stats;
+        notification.stats.updateFrom(old.stats);
         AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
         for (AggregatedStats stats : aggregatedStatsArray) {
             stats.numUpdatedByApp++;
             stats.countApiUse(notification);
         }
         releaseAggregatedStatsLocked(aggregatedStatsArray);
+        if (ENABLE_SQLITE_LOG) {
+            mSQLiteLog.logPosted(notification);
+        }
     }
 
     /**
@@ -297,10 +301,6 @@
         public int numWithActions;
         public int numPrivate;
         public int numSecret;
-        public int numPriorityMax;
-        public int numPriorityHigh;
-        public int numPriorityLow;
-        public int numPriorityMin;
         public int numWithBigText;
         public int numWithBigPicture;
         public int numForegroundService;
@@ -314,11 +314,17 @@
         public int numWithSubText;
         public int numWithInfoText;
         public int numInterrupt;
+        public ImportanceHistogram noisyImportance;
+        public ImportanceHistogram quietImportance;
+        public ImportanceHistogram finalImportance;
 
         public AggregatedStats(Context context, String key) {
             this.key = key;
             mContext = context;
             mCreated = SystemClock.elapsedRealtime();
+            noisyImportance = new ImportanceHistogram(context, "note_imp_noisy_");
+            quietImportance = new ImportanceHistogram(context, "note_imp_quiet_");
+            finalImportance = new ImportanceHistogram(context, "note_importance_");
         }
 
         public void countApiUse(NotificationRecord record) {
@@ -354,20 +360,12 @@
                     break;
             }
 
-            switch (n.priority) {
-                case Notification.PRIORITY_MAX:
-                    numPriorityMax++;
-                    break;
-                case Notification.PRIORITY_HIGH:
-                    numPriorityHigh++;
-                    break;
-                case Notification.PRIORITY_LOW:
-                    numPriorityLow++;
-                    break;
-                case Notification.PRIORITY_MIN:
-                    numPriorityMin++;
-                    break;
+            if (record.stats.isNoisy) {
+                noisyImportance.increment(record.stats.requestedImportance);
+            } else {
+                quietImportance.increment(record.stats.requestedImportance);
             }
+            finalImportance.increment(record.getImportance());
 
             final Set<String> names = n.extras.keySet();
             if (names.contains(Notification.EXTRA_BIG_TEXT)) {
@@ -419,10 +417,6 @@
             maybeCount("note_with_actions", (numWithActions - mPrevious.numWithActions));
             maybeCount("note_private", (numPrivate - mPrevious.numPrivate));
             maybeCount("note_secret", (numSecret - mPrevious.numSecret));
-            maybeCount("note_prio_max", (numPriorityMax - mPrevious.numPriorityMax));
-            maybeCount("note_prio_high", (numPriorityHigh - mPrevious.numPriorityHigh));
-            maybeCount("note_prio_low", (numPriorityLow - mPrevious.numPriorityLow));
-            maybeCount("note_prio_min", (numPriorityMin - mPrevious.numPriorityMin));
             maybeCount("note_interupt", (numInterrupt - mPrevious.numInterrupt));
             maybeCount("note_big_text", (numWithBigText - mPrevious.numWithBigText));
             maybeCount("note_big_pic", (numWithBigPicture - mPrevious.numWithBigPicture));
@@ -436,6 +430,9 @@
             maybeCount("note_text", (numWithText - mPrevious.numWithText));
             maybeCount("note_sub_text", (numWithSubText - mPrevious.numWithSubText));
             maybeCount("note_info_text", (numWithInfoText - mPrevious.numWithInfoText));
+            noisyImportance.maybeCount(mPrevious.noisyImportance);
+            quietImportance.maybeCount(mPrevious.quietImportance);
+            finalImportance.maybeCount(mPrevious.finalImportance);
 
             mPrevious.numPostedByApp = numPostedByApp;
             mPrevious.numUpdatedByApp = numUpdatedByApp;
@@ -448,10 +445,6 @@
             mPrevious.numWithActions = numWithActions;
             mPrevious.numPrivate = numPrivate;
             mPrevious.numSecret = numSecret;
-            mPrevious.numPriorityMax = numPriorityMax;
-            mPrevious.numPriorityHigh = numPriorityHigh;
-            mPrevious.numPriorityLow = numPriorityLow;
-            mPrevious.numPriorityMin = numPriorityMin;
             mPrevious.numInterrupt = numInterrupt;
             mPrevious.numWithBigText = numWithBigText;
             mPrevious.numWithBigPicture = numWithBigPicture;
@@ -465,6 +458,9 @@
             mPrevious.numWithText = numWithText;
             mPrevious.numWithSubText = numWithSubText;
             mPrevious.numWithInfoText = numWithInfoText;
+            noisyImportance.update(mPrevious.noisyImportance);
+            quietImportance.update(mPrevious.quietImportance);
+            finalImportance.update(mPrevious.finalImportance);
         }
 
         void maybeCount(String name, int value) {
@@ -483,17 +479,64 @@
         }
 
         private String toStringWithIndent(String indent) {
-            return indent + "AggregatedStats{\n" +
-                    indent + "  key='" + key + "',\n" +
-                    indent + "  numPostedByApp=" + numPostedByApp + ",\n" +
-                    indent + "  numUpdatedByApp=" + numUpdatedByApp + ",\n" +
-                    indent + "  numRemovedByApp=" + numRemovedByApp + ",\n" +
-                    indent + "  numPeopleCacheHit=" + numPeopleCacheHit + ",\n" +
-                    indent + "  numWithStaredPeople=" + numWithStaredPeople + ",\n" +
-                    indent + "  numWithValidPeople=" + numWithValidPeople + ",\n" +
-                    indent + "  numPeopleCacheMiss=" + numPeopleCacheMiss + ",\n" +
-                    indent + "  numBlocked=" + numBlocked + ",\n" +
-                    indent + "}";
+            StringBuilder output = new StringBuilder();
+            output.append(indent).append("AggregatedStats{\n");
+            String indentPlusTwo = indent + "  ";
+            output.append(indentPlusTwo);
+            output.append("key='").append(key).append("',\n");
+            output.append(indentPlusTwo);
+            output.append("numPostedByApp=").append(numPostedByApp).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numUpdatedByApp=").append(numUpdatedByApp).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numRemovedByApp=").append(numRemovedByApp).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numPeopleCacheHit=").append(numPeopleCacheHit).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numWithStaredPeople=").append(numWithStaredPeople).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numWithValidPeople=").append(numWithValidPeople).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numPeopleCacheMiss=").append(numPeopleCacheMiss).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numBlocked=").append(numBlocked).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numWithActions=").append(numWithActions).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numPrivate=").append(numPrivate).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numSecret=").append(numSecret).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numInterrupt=").append(numInterrupt).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numWithBigText=").append(numWithBigText).append(",\n");
+            output.append(indentPlusTwo);
+            output.append("numWithBigPicture=").append(numWithBigPicture).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numForegroundService=").append(numForegroundService).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numOngoing=").append(numOngoing).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numAutoCancel=").append(numAutoCancel).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numWithLargeIcon=").append(numWithLargeIcon).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numWithInbox=").append(numWithInbox).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numWithMediaSession=").append(numWithMediaSession).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numWithTitle=").append(numWithTitle).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numWithText=").append(numWithText).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numWithSubText=").append(numWithSubText).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numWithInfoText=").append(numWithInfoText).append("\n");
+            output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n");
+            output.append(indentPlusTwo).append(quietImportance.toString()).append("\n");
+            output.append(indentPlusTwo).append(finalImportance.toString()).append("\n");
+            output.append(indent).append("}");
+            return output.toString();
         }
 
         public JSONObject dumpJson() throws JSONException {
@@ -511,10 +554,6 @@
             maybePut(dump, "numWithActions", numWithActions);
             maybePut(dump, "numPrivate", numPrivate);
             maybePut(dump, "numSecret", numSecret);
-            maybePut(dump, "numPriorityMax", numPriorityMax);
-            maybePut(dump, "numPriorityHigh", numPriorityHigh);
-            maybePut(dump, "numPriorityLow", numPriorityLow);
-            maybePut(dump, "numPriorityMin", numPriorityMin);
             maybePut(dump, "numInterrupt", numInterrupt);
             maybePut(dump, "numWithBigText", numWithBigText);
             maybePut(dump, "numWithBigPicture", numWithBigPicture);
@@ -528,6 +567,10 @@
             maybePut(dump, "numWithText", numWithText);
             maybePut(dump, "numWithSubText", numWithSubText);
             maybePut(dump, "numWithInfoText", numWithInfoText);
+            noisyImportance.maybePut(dump, mPrevious.noisyImportance);
+            quietImportance.maybePut(dump, mPrevious.quietImportance);
+            finalImportance.maybePut(dump, mPrevious.finalImportance);
+
             return dump;
         }
 
@@ -538,6 +581,65 @@
         }
     }
 
+    private static class ImportanceHistogram {
+        // TODO define these somewhere else
+        private static final int NUM_IMPORTANCES = 5;
+        private static final String[] IMPORTANCE_NAMES = {"none", "low", "default", "high", "max"};
+        private final Context mContext;
+        private final String[] mCounterNames;
+        private final String mPrefix;
+        private int[] mCount;
+
+        ImportanceHistogram(Context context, String prefix) {
+            mContext = context;
+            mCount = new int[NUM_IMPORTANCES];
+            mCounterNames = new String[NUM_IMPORTANCES];
+            mPrefix = prefix;
+            for (int i = 0; i < NUM_IMPORTANCES; i++) {
+                mCounterNames[i] = mPrefix + IMPORTANCE_NAMES[i];
+            }
+        }
+
+        void increment(int imp) {
+            imp = imp < 0 ? 0 : imp > NUM_IMPORTANCES ? NUM_IMPORTANCES : imp;
+            mCount[imp] ++;
+        }
+
+        void maybeCount(ImportanceHistogram prev) {
+            for (int i = 0; i < NUM_IMPORTANCES; i++) {
+                final int value = mCount[i] - prev.mCount[i];
+                if (value > 0) {
+                    MetricsLogger.count(mContext, mCounterNames[i], value);
+                }
+            }
+        }
+
+        void update(ImportanceHistogram that) {
+            for (int i = 0; i < NUM_IMPORTANCES; i++) {
+                mCount[i] = that.mCount[i];
+            }
+        }
+
+        public void maybePut(JSONObject dump, ImportanceHistogram prev)
+                throws JSONException {
+            dump.put(mPrefix, new JSONArray(mCount));
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder output = new StringBuilder();
+            output.append(mPrefix).append(": [");
+            for (int i = 0; i < NUM_IMPORTANCES; i++) {
+                output.append(mCount[i]);
+                if (i < (NUM_IMPORTANCES-1)) {
+                    output.append(", ");
+                }
+            }
+            output.append("]");
+            return output.toString();
+        }
+    }
+
     /**
      * Tracks usage of an individual notification that is currently active.
      */
@@ -575,6 +677,12 @@
         public long airtimeExpandedMs = 0;
         /** Number of times the notification has been expanded by the user. */
         public long userExpansionCount = 0;
+        /** Importance directly requested by the app. */
+        public int requestedImportance;
+        /** Did the app include sound or vibration on the notificaiton. */
+        public boolean isNoisy;
+        /** Importance after initial filtering for noise and other features */
+        public int naturalImportance;
 
         public long getCurrentPosttimeMs() {
             if (posttimeElapsedMs < 0) {
@@ -686,17 +794,40 @@
 
         @Override
         public String toString() {
-            return "SingleNotificationStats{" +
-                    "posttimeElapsedMs=" + posttimeElapsedMs +
-                    ", posttimeToFirstClickMs=" + posttimeToFirstClickMs +
-                    ", posttimeToDismissMs=" + posttimeToDismissMs +
-                    ", airtimeCount=" + airtimeCount +
-                    ", airtimeMs=" + airtimeMs +
-                    ", currentAirtimeStartElapsedMs=" + currentAirtimeStartElapsedMs +
-                    ", airtimeExpandedMs=" + airtimeExpandedMs +
-                    ", posttimeToFirstVisibleExpansionMs=" + posttimeToFirstVisibleExpansionMs +
-                    ", currentAirtimeExpandedSEMs=" + currentAirtimeExpandedStartElapsedMs +
-                    '}';
+            StringBuilder output = new StringBuilder();
+            output.append("SingleNotificationStats{");
+
+            output.append("posttimeElapsedMs=").append(posttimeElapsedMs).append(", ");
+            output.append("posttimeToFirstClickMs=").append(posttimeToFirstClickMs).append(", ");
+            output.append("posttimeToDismissMs=").append(posttimeToDismissMs).append(", ");
+            output.append("airtimeCount=").append(airtimeCount).append(", ");
+            output.append("airtimeMs=").append(airtimeMs).append(", ");
+            output.append("currentAirtimeStartElapsedMs=").append(currentAirtimeStartElapsedMs)
+                    .append(", ");
+            output.append("airtimeExpandedMs=").append(airtimeExpandedMs).append(", ");
+            output.append("posttimeToFirstVisibleExpansionMs=")
+                    .append(posttimeToFirstVisibleExpansionMs).append(", ");
+            output.append("currentAirtimeExpandedStartElapsedMs=")
+                    .append(currentAirtimeExpandedStartElapsedMs).append(", ");
+            output.append("requestedImportance=").append(requestedImportance).append(", ");
+            output.append("naturalImportance=").append(naturalImportance).append(", ");
+            output.append("isNoisy=").append(isNoisy);
+            output.append('}');
+            return output.toString();
+        }
+
+        /** Copy useful information out of the stats from the pre-update notifications. */
+        public void updateFrom(SingleNotificationStats old) {
+            posttimeElapsedMs = old.posttimeElapsedMs;
+            posttimeToFirstClickMs = old.posttimeToFirstClickMs;
+            airtimeCount = old.airtimeCount;
+            posttimeToFirstAirtimeMs = old.posttimeToFirstAirtimeMs;
+            currentAirtimeStartElapsedMs = old.currentAirtimeStartElapsedMs;
+            airtimeMs = old.airtimeMs;
+            posttimeToFirstVisibleExpansionMs = old.posttimeToFirstVisibleExpansionMs;
+            currentAirtimeExpandedStartElapsedMs = old.currentAirtimeExpandedStartElapsedMs;
+            airtimeExpandedMs = old.airtimeExpandedMs;
+            userExpansionCount = old.userExpansionCount;
         }
     }
 
@@ -741,7 +872,7 @@
         private static final int MSG_DISMISS = 4;
 
         private static final String DB_NAME = "notification_log.db";
-        private static final int DB_VERSION = 4;
+        private static final int DB_VERSION = 5;
 
         /** Age in ms after which events are pruned from the DB. */
         private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L;  // 1 week
@@ -762,7 +893,11 @@
         private static final String COL_WHEN_MS = "when_ms";
         private static final String COL_DEFAULTS = "defaults";
         private static final String COL_FLAGS = "flags";
-        private static final String COL_PRIORITY = "priority";
+        private static final String COL_IMPORTANCE_REQ = "importance_request";
+        private static final String COL_IMPORTANCE_FINAL = "importance_final";
+        private static final String COL_NOISY = "noisy";
+        private static final String COL_MUTED = "muted";
+        private static final String COL_DEMOTED = "demoted";
         private static final String COL_CATEGORY = "category";
         private static final String COL_ACTION_COUNT = "action_count";
         private static final String COL_POSTTIME_MS = "posttime_ms";
@@ -776,14 +911,28 @@
         private static final int EVENT_TYPE_CLICK = 2;
         private static final int EVENT_TYPE_REMOVE = 3;
         private static final int EVENT_TYPE_DISMISS = 4;
-
         private static long sLastPruneMs;
+
         private static long sNumWrites;
-
         private final SQLiteOpenHelper mHelper;
-        private final Handler mWriteHandler;
 
+        private final Handler mWriteHandler;
         private static final long DAY_MS = 24 * 60 * 60 * 1000;
+        private static final String STATS_QUERY = "SELECT " +
+                COL_EVENT_USER_ID + ", " +
+                COL_PKG + ", " +
+                // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)'
+                "CAST(((%d - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
+                "AS day, " +
+                "COUNT(*) AS cnt, " +
+                "SUM(" + COL_MUTED + ") as muted, " +
+                "SUM(" + COL_NOISY + ") as noisy, " +
+                "SUM(" + COL_DEMOTED + ") as demoted " +
+                "FROM " + TAB_LOG + " " +
+                "WHERE " +
+                COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
+                " AND " + COL_EVENT_TIME + " > %d " +
+                " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
 
         public SQLiteLog(Context context) {
             HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
@@ -828,7 +977,11 @@
                             COL_WHEN_MS + " INT," +
                             COL_DEFAULTS + " INT," +
                             COL_FLAGS + " INT," +
-                            COL_PRIORITY + " INT," +
+                            COL_IMPORTANCE_REQ + " INT," +
+                            COL_IMPORTANCE_FINAL + " INT," +
+                            COL_NOISY + " INT," +
+                            COL_MUTED + " INT," +
+                            COL_DEMOTED + " INT," +
                             COL_CATEGORY + " TEXT," +
                             COL_ACTION_COUNT + " INT," +
                             COL_POSTTIME_MS + " INT," +
@@ -841,8 +994,7 @@
 
                 @Override
                 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-                    if (oldVersion <= 3) {
-                        // Version 3 creation left 'log' in a weird state. Just reset for now.
+                    if (oldVersion != newVersion) {
                         db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG);
                         onCreate(db);
                     }
@@ -866,22 +1018,11 @@
             mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification));
         }
 
-        private JSONArray JsonPostFrequencies(DumpFilter filter) throws JSONException {
+        private JSONArray jsonPostFrequencies(DumpFilter filter) throws JSONException {
             JSONArray frequencies = new JSONArray();
             SQLiteDatabase db = mHelper.getReadableDatabase();
             long midnight = getMidnightMs();
-            String q = "SELECT " +
-                    COL_EVENT_USER_ID + ", " +
-                    COL_PKG + ", " +
-                    // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)'
-                    "CAST(((" + midnight + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
-                    "AS day, " +
-                    "COUNT(*) AS cnt " +
-                    "FROM " + TAB_LOG + " " +
-                    "WHERE " +
-                    COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
-                    " AND " + COL_EVENT_TIME + " > " + filter.since +
-                    " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
+            String q = String.format(STATS_QUERY, midnight, filter.since);
             Cursor cursor = db.rawQuery(q, null);
             try {
                 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
@@ -890,11 +1031,17 @@
                     if (filter != null && !filter.matches(pkg)) continue;
                     int day = cursor.getInt(2);
                     int count = cursor.getInt(3);
+                    int muted = cursor.getInt(4);
+                    int noisy = cursor.getInt(5);
+                    int demoted = cursor.getInt(6);
                     JSONObject row = new JSONObject();
                     row.put("user_id", userId);
                     row.put("package", pkg);
                     row.put("day", day);
                     row.put("count", count);
+                    row.put("noisy", noisy);
+                    row.put("muted", muted);
+                    row.put("demoted", demoted);
                     frequencies.put(row);
                 }
             } finally {
@@ -906,17 +1053,7 @@
         public void printPostFrequencies(PrintWriter pw, String indent, DumpFilter filter) {
             SQLiteDatabase db = mHelper.getReadableDatabase();
             long midnight = getMidnightMs();
-            String q = "SELECT " +
-                    COL_EVENT_USER_ID + ", " +
-                    COL_PKG + ", " +
-                    // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)'
-                    "CAST(((" + midnight + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
-                        "AS day, " +
-                    "COUNT(*) AS cnt " +
-                    "FROM " + TAB_LOG + " " +
-                    "WHERE " +
-                    COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + " " +
-                    "GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
+            String q = String.format(STATS_QUERY, midnight, filter.since);
             Cursor cursor = db.rawQuery(q, null);
             try {
                 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
@@ -925,8 +1062,12 @@
                     if (filter != null && !filter.matches(pkg)) continue;
                     int day = cursor.getInt(2);
                     int count = cursor.getInt(3);
+                    int muted = cursor.getInt(4);
+                    int noisy = cursor.getInt(5);
+                    int demoted = cursor.getInt(6);
                     pw.println(indent + "post_frequency{user_id=" + userId + ",pkg=" + pkg +
-                            ",day=" + day + ",count=" + count + "}");
+                            ",day=" + day + ",count=" + count + ",muted=" + muted + "/" + noisy +
+                            ",demoted=" + demoted + "}");
                 }
             } finally {
                 cursor.close();
@@ -985,7 +1126,18 @@
             }
             outCv.put(COL_WHEN_MS, r.sbn.getPostTime());
             outCv.put(COL_FLAGS, r.getNotification().flags);
-            outCv.put(COL_PRIORITY, r.getNotification().priority);
+            final int before = r.stats.requestedImportance;
+            final int after = r.getImportance();
+            final boolean noisy = r.stats.isNoisy;
+            outCv.put(COL_IMPORTANCE_REQ, before);
+            outCv.put(COL_IMPORTANCE_FINAL, after);
+            outCv.put(COL_DEMOTED, after < before ? 1 : 0);
+            outCv.put(COL_NOISY, noisy);
+            if (noisy && after < IMPORTANCE_HIGH) {
+                outCv.put(COL_MUTED, 1);
+            } else {
+                outCv.put(COL_MUTED, 0);
+            }
             if (r.getNotification().category != null) {
                 outCv.put(COL_CATEGORY, r.getNotification().category);
             }
@@ -1008,7 +1160,9 @@
         public JSONObject dumpJson(DumpFilter filter) {
             JSONObject dump = new JSONObject();
             try {
-                dump.put("post_frequency", JsonPostFrequencies(filter));
+                dump.put("post_frequency", jsonPostFrequencies(filter));
+                dump.put("since", filter.since);
+                dump.put("now", System.currentTimeMillis());
             } catch (JSONException e) {
                 // pass
             }
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 9b10ef2..7f85e1f 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -23,20 +23,18 @@
 
     List<Notification.Topic> getTopics(String packageName, int uid);
 
-    int getTopicPriority(String packageName, int uid, Notification.Topic topic);
+    int getPriority(String packageName, int uid, Notification.Topic topic);
 
-    void setTopicPriority(String packageName, int uid, Notification.Topic topic, int priority);
+    void setPriority(String packageName, int uid, Notification.Topic topic, int priority);
 
-    int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic);
+    int getVisibilityOverride(String packageName, int uid, Notification.Topic topic);
 
-    void setTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic,
+    void setVisibilityOverride(String packageName, int uid, Notification.Topic topic,
             int visibility);
 
-    void setTopicImportance(String packageName, int uid, Notification.Topic topic, int importance);
+    void setImportance(String packageName, int uid, Notification.Topic topic, int importance);
 
-    int getTopicImportance(String packageName, int uid, Notification.Topic topic);
-
-    void setAppImportance(String packageName, int uid, int importance);
+    int getImportance(String packageName, int uid, Notification.Topic topic);
 
     boolean doesAppUseTopics(String packageName, int uid);
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index ce4ecd3..827482f 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -164,6 +164,8 @@
                             r = getOrCreateRecord(name, uid);
                         }
                         r.importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+                        r.priority = priority;
+                        r.visibility = vis;
 
                         // Migrate package level settings to the default topic.
                         // Might be overwritten by parseTopics.
@@ -245,7 +247,15 @@
             }
             out.startTag(null, TAG_PACKAGE);
             out.attribute(null, ATT_NAME, r.pkg);
-            out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
+            if (r.importance != DEFAULT_IMPORTANCE) {
+                out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
+            }
+            if (r.priority != DEFAULT_PRIORITY) {
+                out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
+            }
+            if (r.visibility != DEFAULT_VISIBILITY) {
+                out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
+            }
 
             if (!forBackup) {
                 out.attribute(null, ATT_UID, Integer.toString(r.uid));
@@ -373,66 +383,109 @@
     public List<Notification.Topic> getTopics(String packageName, int uid) {
         final Record r = getOrCreateRecord(packageName, uid);
         List<Notification.Topic> topics = new ArrayList<>();
-        for (Topic t :  r.topics.values()) {
+        for (Topic t : r.topics.values()) {
             topics.add(t.topic);
         }
         return topics;
     }
 
+    /**
+     * Gets priority. If a topic is given, returns the priority of that topic. Otherwise, the
+     * priority of the app.
+     */
     @Override
-    public int getTopicPriority(String packageName, int uid, Notification.Topic topic) {
+    public int getPriority(String packageName, int uid, Notification.Topic topic) {
         final Record r = getOrCreateRecord(packageName, uid);
+        if (topic == null) {
+            return r.priority;
+        }
         return getOrCreateTopic(r, topic).priority;
     }
 
+    /**
+     * Sets priority. If a topic is given, sets the priority of that topic. If not,
+     * sets the default priority for all new topics that appear in the future, and resets
+     * the priority of all current topics.
+     */
     @Override
-    public void setTopicPriority(String packageName, int uid, Notification.Topic topic,
+    public void setPriority(String packageName, int uid, Notification.Topic topic,
             int priority) {
         final Record r = getOrCreateRecord(packageName, uid);
-        getOrCreateTopic(r, topic).priority = priority;
-        updateConfig();
-    }
-
-    @Override
-    public int getTopicVisibilityOverride(String packageName, int uid, Notification.Topic topic) {
-        final Record r = getOrCreateRecord(packageName, uid);
-        return getOrCreateTopic(r, topic).visibility;
-    }
-
-    @Override
-    public void setTopicVisibilityOverride(String pkgName, int uid, Notification.Topic topic,
-        int visibility) {
-        final Record r = getOrCreateRecord(pkgName, uid);
-        getOrCreateTopic(r, topic).visibility = visibility;
-        updateConfig();
-    }
-
-    @Override
-    public int getTopicImportance(String packageName, int uid, Notification.Topic topic) {
-        final Record r = getOrCreateRecord(packageName, uid);
-        return getOrCreateTopic(r, topic).importance;
-    }
-
-    @Override
-    public void setTopicImportance(String pkgName, int uid, Notification.Topic topic,
-            int importance) {
-        final Record r = getOrCreateRecord(pkgName, uid);
-        getOrCreateTopic(r, topic).importance = importance;
+        if (topic == null) {
+            r.priority = priority;
+            for (Topic t : r.topics.values()) {
+                t.priority = priority;
+            }
+        } else {
+            getOrCreateTopic(r, topic).priority = priority;
+        }
         updateConfig();
     }
 
     /**
-     * Sets the default importance for all new topics that appear in the future, and resets
+     * Gets visual override. If a topic is given, returns the override of that topic. Otherwise, the
+     * override of the app.
+     */
+    @Override
+    public int getVisibilityOverride(String packageName, int uid, Notification.Topic topic) {
+        final Record r = getOrCreateRecord(packageName, uid);
+        if (topic == null) {
+            return r.visibility;
+        }
+        return getOrCreateTopic(r, topic).visibility;
+    }
+
+    /**
+     * Sets visibility override. If a topic is given, sets the override of that topic. If not,
+     * sets the default override for all new topics that appear in the future, and resets
+     * the override of all current topics.
+     */
+    @Override
+    public void setVisibilityOverride(String pkgName, int uid, Notification.Topic topic,
+        int visibility) {
+        final Record r = getOrCreateRecord(pkgName, uid);
+        if (topic == null) {
+            r.visibility = visibility;
+            for (Topic t : r.topics.values()) {
+                t.visibility = visibility;
+            }
+        } else {
+            getOrCreateTopic(r, topic).visibility = visibility;
+        }
+        updateConfig();
+    }
+
+    /**
+     * Gets importance. If a topic is given, returns the importance of that topic. Otherwise, the
+     * importance of the app.
+     */
+    @Override
+    public int getImportance(String packageName, int uid, Notification.Topic topic) {
+        final Record r = getOrCreateRecord(packageName, uid);
+        if (topic == null) {
+            return r.importance;
+        }
+        return getOrCreateTopic(r, topic).importance;
+    }
+
+    /**
+     * Sets importance. If a topic is given, sets the importance of that topic. If not, sets the
+     * default importance for all new topics that appear in the future, and resets
      * the importance of all current topics (unless the app is being blocked).
      */
     @Override
-    public void setAppImportance(String pkgName, int uid, int importance) {
+    public void setImportance(String pkgName, int uid, Notification.Topic topic,
+            int importance) {
         final Record r = getOrCreateRecord(pkgName, uid);
-        r.importance = importance;
-        if (Ranking.IMPORTANCE_NONE != importance) {
-            for (Topic t : r.topics.values()) {
-                t.importance = importance;
+        if (topic == null) {
+            r.importance = importance;
+            if (Ranking.IMPORTANCE_NONE != importance) {
+                for (Topic t : r.topics.values()) {
+                    t.importance = importance;
+                }
             }
+        } else {
+            getOrCreateTopic(r, topic).importance = importance;
         }
         updateConfig();
     }
@@ -459,6 +512,8 @@
         } else {
             t = new Topic(topic);
             t.importance = r.importance;
+            t.priority = r.priority;
+            t.visibility = r.visibility;
             r.topics.put(topic.getId(), t);
             return t;
         }
@@ -503,8 +558,18 @@
                 pw.print(" (");
                 pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
                 pw.print(')');
-                pw.print(" importance=");
-                pw.print(Ranking.importanceToString(r.importance));
+                if (r.importance != DEFAULT_IMPORTANCE) {
+                    pw.print(" importance=");
+                    pw.print(Ranking.importanceToString(r.importance));
+                }
+                if (r.priority != DEFAULT_PRIORITY) {
+                    pw.print(" priority=");
+                    pw.print(Ranking.importanceToString(r.priority));
+                }
+                if (r.visibility != DEFAULT_VISIBILITY) {
+                    pw.print(" visibility=");
+                    pw.print(Ranking.importanceToString(r.visibility));
+                }
                 pw.println();
                 for (Topic t : r.topics.values()) {
                     pw.print(prefix);
@@ -561,6 +626,8 @@
         String pkg;
         int uid = UNKNOWN_UID;
         int importance = DEFAULT_IMPORTANCE;
+        int priority = DEFAULT_PRIORITY;
+        int visibility = DEFAULT_VISIBILITY;
         Map<String, Topic> topics = new ArrayMap<>();
    }
 
diff --git a/services/core/java/com/android/server/notification/TopicImportanceExtractor.java b/services/core/java/com/android/server/notification/TopicImportanceExtractor.java
index 01770d0..c6b3e0f 100644
--- a/services/core/java/com/android/server/notification/TopicImportanceExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicImportanceExtractor.java
@@ -42,7 +42,7 @@
             return null;
         }
 
-        final int topicImportance = mConfig.getTopicImportance(record.sbn.getPackageName(),
+        final int topicImportance = mConfig.getImportance(record.sbn.getPackageName(),
                 record.sbn.getUid(), record.sbn.getNotification().getTopic());
         record.setTopicImportance(topicImportance);
 
diff --git a/services/core/java/com/android/server/notification/TopicPriorityExtractor.java b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
index 5bf989ae..1df5c2b 100644
--- a/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicPriorityExtractor.java
@@ -42,7 +42,7 @@
             return null;
         }
 
-        final int packagePriority = mConfig.getTopicPriority(record.sbn.getPackageName(),
+        final int packagePriority = mConfig.getPriority(record.sbn.getPackageName(),
                 record.sbn.getUid(), record.sbn.getNotification().getTopic());
         record.setPackagePriority(packagePriority);
 
diff --git a/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
index e053382..eaa3ed3 100644
--- a/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
+++ b/services/core/java/com/android/server/notification/TopicVisibilityExtractor.java
@@ -42,7 +42,7 @@
             return null;
         }
 
-        final int packageVisibility = mConfig.getTopicVisibilityOverride(
+        final int packageVisibility = mConfig.getVisibilityOverride(
                 record.sbn.getPackageName(), record.sbn.getUid(),
                 record.sbn.getNotification().getTopic());
         record.setPackageVisibilityOverride(packageVisibility);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 3193ff8..3ca99d4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -435,7 +435,11 @@
         for (SessionState state : userState.sessionStateMap.values()) {
             if (state.session != null) {
                 try {
-                    state.session.release();
+                    if (state.isRecordingSession) {
+                        state.session.disconnect();
+                    } else {
+                        state.session.release();
+                    }
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in release", e);
                 }
@@ -604,7 +608,11 @@
 
         // Create a session. When failed, send a null token immediately.
         try {
-            service.createSession(channels[1], callback, sessionState.info.getId());
+            if (sessionState.isRecordingSession) {
+                service.createRecordingSession(callback, sessionState.info.getId());
+            } else {
+                service.createSession(channels[1], callback, sessionState.info.getId());
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
@@ -632,7 +640,11 @@
                 if (sessionToken == userState.mainSessionToken) {
                     setMainLocked(sessionToken, false, callingUid, userId);
                 }
-                sessionState.session.release();
+                if (sessionState.isRecordingSession) {
+                    sessionState.session.disconnect();
+                } else {
+                    sessionState.session.release();
+                }
             }
         } catch (RemoteException | SessionNotFoundException e) {
             Slog.e(TAG, "error in releaseSession", e);
@@ -766,6 +778,21 @@
         }
     }
 
+    private void notifyTvInputInfoChanged(UserState userState, String inputId,
+            TvInputInfo inputInfo) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyTvInputInfoChanged(inputId=" + inputId + ", inputInfo=" + inputInfo
+                    + ")");
+        }
+        for (ITvInputManagerCallback callback : userState.callbackSet) {
+            try {
+                callback.onTvInputInfoChanged(inputId, inputInfo);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "failed to report changed input info to callback", e);
+            }
+        }
+    }
+
     private void setStateLocked(String inputId, int state, int userId) {
         UserState userState = getOrCreateUserStateLocked(userId);
         TvInputState inputState = userState.inputMap.get(inputId);
@@ -1005,7 +1032,7 @@
 
         @Override
         public void createSession(final ITvInputClient client, final String inputId,
-                int seq, int userId) {
+                boolean isRecordingSession, int seq, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "createSession");
@@ -1033,8 +1060,8 @@
 
                     // Create a new session token and a session state.
                     IBinder sessionToken = new Binder();
-                    SessionState sessionState = new SessionState(sessionToken, info, client,
-                            seq, callingUid, resolvedUserId);
+                    SessionState sessionState = new SessionState(sessionToken, info,
+                            isRecordingSession, client, seq, callingUid, resolvedUserId);
 
                     // Add them to the global session state map of the current user.
                     userState.sessionStateMap.put(sessionToken, sessionState);
@@ -1375,6 +1402,26 @@
         }
 
         @Override
+        public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "timeShiftPlay");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPlay(
+                                recordedProgramUri);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in timeShiftPlay", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void timeShiftPause(IBinder sessionToken, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -1383,8 +1430,7 @@
             try {
                 synchronized (mLock) {
                     try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
-                                .timeShiftPause();
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).timeShiftPause();
                     } catch (RemoteException | SessionNotFoundException e) {
                         Slog.e(TAG, "error in timeShiftPause", e);
                     }
@@ -1477,6 +1523,64 @@
         }
 
         @Override
+        public void connect(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "connect");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).connect(
+                                channelUri, params);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in connect", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void startRecording(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "startRecording");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in startRecording", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void stopRecording(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+                    userId, "stopRecording");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).stopRecording();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in stopRecording", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
             if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -1912,6 +2016,7 @@
 
     private final class SessionState implements IBinder.DeathRecipient {
         private final TvInputInfo info;
+        private final boolean isRecordingSession;
         private final ITvInputClient client;
         private final int seq;
         private final int callingUid;
@@ -1922,10 +2027,11 @@
         // Not null if this session represents an external device connected to a hardware TV input.
         private IBinder hardwareSessionToken;
 
-        private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
-                int seq, int callingUid, int userId) {
+        private SessionState(IBinder sessionToken, TvInputInfo info, boolean isRecordingSession,
+                ITvInputClient client, int seq, int callingUid, int userId) {
             this.sessionToken = sessionToken;
             this.info = info;
+            this.isRecordingSession = isRecordingSession;
             this.client = client;
             this.seq = seq;
             this.callingUid = callingUid;
@@ -2126,6 +2232,18 @@
                 }
             }
         }
+
+        @Override
+        public void setTvInputInfo(String inputId, TvInputInfo inputInfo) {
+            ensureValidInput(inputInfo);
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "setTvInputInfo(" + inputInfo + ")");
+                }
+                UserState userState = getOrCreateUserStateLocked(mUserId);
+                notifyTvInputInfoChanged(userState, inputId, inputInfo);
+            }
+        }
     }
 
     private final class SessionCallback extends ITvInputSessionCallback.Stub {
@@ -2393,6 +2511,78 @@
                 }
             }
         }
+
+        // For the recording session only
+        @Override
+        public void onConnected() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onConnected()");
+                }
+                if (mSessionState.session == null || mSessionState.client == null) {
+                    return;
+                }
+                try {
+                    mSessionState.client.onConnected(mSessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onConnected", e);
+                }
+            }
+        }
+
+        // For the recording session only
+        @Override
+        public void onRecordingStarted() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onRecordingStarted()");
+                }
+                if (mSessionState.session == null || mSessionState.client == null) {
+                    return;
+                }
+                try {
+                    mSessionState.client.onRecordingStarted(mSessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onRecordingStarted", e);
+                }
+            }
+        }
+
+        // For the recording session only
+        @Override
+        public void onRecordingStopped(Uri recordedProgramUri) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onRecordingStopped()");
+                }
+                if (mSessionState.session == null || mSessionState.client == null) {
+                    return;
+                }
+                try {
+                    mSessionState.client.onRecordingStopped(recordedProgramUri, mSessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onRecordingStopped", e);
+                }
+            }
+        }
+
+        // For the recording session only
+        @Override
+        public void onError(int error) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onError()");
+                }
+                if (mSessionState.session == null || mSessionState.client == null) {
+                    return;
+                }
+                try {
+                    mSessionState.client.onError(error, mSessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onError", e);
+                }
+            }
+        }
     }
 
     private static final class WatchLogHandler extends Handler {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e3e90b66..04820a1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -202,6 +202,7 @@
 
     private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
     private static final String ATTR_SETUP_COMPLETE = "setup-complete";
+    private static final String ATTR_PROVISIONING_STATE = "provisioning-state";
     private static final String ATTR_PERMISSION_POLICY = "permission-policy";
 
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
@@ -364,6 +365,7 @@
         int mPasswordOwner = -1;
         long mLastMaximumTimeToLock = -1;
         boolean mUserSetupComplete = false;
+        int mUserProvisioningState;
         int mPermissionPolicy;
 
         final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
@@ -2017,6 +2019,10 @@
                 out.attribute(null, ATTR_SETUP_COMPLETE,
                         Boolean.toString(true));
             }
+            if (policy.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) {
+                out.attribute(null, ATTR_PROVISIONING_STATE,
+                        Integer.toString(policy.mUserProvisioningState));
+            }
             if (policy.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
                 out.attribute(null, ATTR_PERMISSION_POLICY,
                         Integer.toString(policy.mPermissionPolicy));
@@ -2167,6 +2173,10 @@
             if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
                 policy.mUserSetupComplete = true;
             }
+            String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE);
+            if (!TextUtils.isEmpty(provisioningState)) {
+                policy.mUserProvisioningState = Integer.parseInt(provisioningState);
+            }
             String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY);
             if (!TextUtils.isEmpty(permissionPolicy)) {
                 policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
@@ -5379,6 +5389,7 @@
         policy.mDelegatedCertInstallerPackage = null;
         policy.mApplicationRestrictionsManagingPackage = null;
         policy.mStatusBarDisabled = false;
+        policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
         saveSettingsLocked(userId);
 
         final long ident = mInjector.binderClearCallingIdentity();
@@ -5406,6 +5417,98 @@
     }
 
     @Override
+    public int getUserProvisioningState() {
+        if (!mHasFeature) {
+            return DevicePolicyManager.STATE_USER_UNMANAGED;
+        }
+        int userHandle = mInjector.userHandleGetCallingUserId();
+        return getUserProvisioningState(userHandle);
+    }
+
+    private int getUserProvisioningState(int userHandle) {
+        return getUserData(userHandle).mUserProvisioningState;
+    }
+
+    @Override
+    public void setUserProvisioningState(int newState, int userHandle) {
+        if (!mHasFeature) {
+            return;
+        }
+
+        if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle)
+                && getManagedUserId(userHandle) == -1) {
+            // No managed device, user or profile, so setting provisioning state makes no sense.
+            throw new IllegalStateException("Not allowed to change provisioning state unless a "
+                      + "device or profile owner is set.");
+        }
+
+        synchronized (this) {
+            boolean transitionCheckNeeded = true;
+
+            // Calling identity/permission checks.
+            final int callingUid = mInjector.binderGetCallingUid();
+            if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+                // ADB shell can only move directly from un-managed to finalized as part of directly
+                // setting profile-owner or device-owner.
+                if (getUserProvisioningState(userHandle) !=
+                        DevicePolicyManager.STATE_USER_UNMANAGED
+                        || newState != DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
+                    throw new IllegalStateException("Not allowed to change provisioning state "
+                            + "unless current provisioning state is unmanaged, and new state is "
+                            + "finalized.");
+                }
+                transitionCheckNeeded = false;
+            } else {
+                // For all other cases, caller must have MANAGE_PROFILE_AND_DEVICE_OWNERS.
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+            }
+
+            final DevicePolicyData policyData = getUserData(userHandle);
+            if (transitionCheckNeeded) {
+                // Optional state transition check for non-ADB case.
+                checkUserProvisioningStateTransition(policyData.mUserProvisioningState, newState);
+            }
+            policyData.mUserProvisioningState = newState;
+            saveSettingsLocked(userHandle);
+        }
+    }
+
+    private void checkUserProvisioningStateTransition(int currentState, int newState) {
+        // Valid transitions for normal use-cases.
+        switch (currentState) {
+            case DevicePolicyManager.STATE_USER_UNMANAGED:
+                // Can move to any state from unmanaged (except itself as an edge case)..
+                if (newState != DevicePolicyManager.STATE_USER_UNMANAGED) {
+                    return;
+                }
+                break;
+            case DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE:
+            case DevicePolicyManager.STATE_USER_SETUP_COMPLETE:
+                // Can only move to finalized from these states.
+                if (newState == DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
+                    return;
+                }
+                break;
+            case DevicePolicyManager.STATE_USER_PROFILE_COMPLETE:
+                // Current user has a managed-profile, but current user is not managed, so
+                // rather than moving to finalized state, go back to unmanaged once
+                // profile provisioning is complete.
+                if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
+                    return;
+                }
+                break;
+            case DevicePolicyManager.STATE_USER_SETUP_FINALIZED:
+                // Cannot transition out of finalized.
+                break;
+        }
+
+        // Didn't meet any of the accepted state transition checks above, throw appropriate error.
+        throw new IllegalStateException("Cannot move to user provisioning state [" + newState + "] "
+                + "from state [" + currentState + "]");
+    }
+
+    @Override
     public void setProfileEnabled(ComponentName who) {
         if (!mHasFeature) {
             return;
@@ -5724,7 +5827,8 @@
             for (int u = 0; u < userCount; u++) {
                 DevicePolicyData policy = getUserData(mUserData.keyAt(u));
                 pw.println();
-                pw.println("  Enabled Device Admins (User " + policy.mUserHandle + "):");
+                pw.println("  Enabled Device Admins (User " + policy.mUserHandle
+                        + ", provisioningState: " + policy.mUserProvisioningState + "):");
                 final int N = policy.mAdminList.size();
                 for (int i=0; i<N; i++) {
                     ActiveAdmin ap = policy.mAdminList.get(i);
@@ -6937,9 +7041,9 @@
 
     @Override
     public void startManagedQuickContact(String actualLookupKey, long actualContactId,
-            long actualDirectoryId, Intent originalIntent) {
-        final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
-                actualLookupKey, actualContactId, actualDirectoryId, originalIntent);
+            boolean isContactIdIgnored, long actualDirectoryId, Intent originalIntent) {
+        final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(actualLookupKey,
+                actualContactId, isContactIdIgnored, actualDirectoryId, originalIntent);
         final int callingUserId = UserHandle.getCallingUserId();
 
         final long ident = mInjector.binderClearCallingIdentity();
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 5abb6e7..e6f4177 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -20,6 +20,7 @@
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.content.ComponentName;
@@ -47,11 +48,11 @@
 import android.print.PrinterId;
 import android.printservice.PrintServiceInfo;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.SparseArray;
 
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
@@ -108,6 +109,10 @@
         @Override
         public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
                 PrintAttributes attributes, String packageName, int appId, int userId) {
+            printJobName = Preconditions.checkStringNotEmpty(printJobName);
+            adapter = Preconditions.checkNotNull(adapter);
+            packageName = Preconditions.checkStringNotEmpty(packageName);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final int resolvedAppId;
             final UserState userState;
@@ -153,6 +158,10 @@
 
         @Override
         public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) {
+            if (printJobId == null) {
+                return null;
+            }
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final int resolvedAppId;
             final UserState userState;
@@ -174,6 +183,8 @@
 
         @Override
         public Icon getCustomPrinterIcon(PrinterId printerId, int userId) {
+            printerId = Preconditions.checkNotNull(printerId);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -193,6 +204,10 @@
 
         @Override
         public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
+            if (printJobId == null) {
+                return;
+            }
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final int resolvedAppId;
             final UserState userState;
@@ -214,6 +229,10 @@
 
         @Override
         public void restartPrintJob(PrintJobId printJobId, int appId, int userId) {
+            if (printJobId == null) {
+                return;
+            }
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final int resolvedAppId;
             final UserState userState;
@@ -279,6 +298,8 @@
         @Override
         public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
                 int userId) {
+            observer = Preconditions.checkNotNull(observer);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -299,6 +320,8 @@
         @Override
         public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
                 int userId) {
+            observer = Preconditions.checkNotNull(observer);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -319,6 +342,12 @@
         @Override
         public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
                 List<PrinterId> priorityList, int userId) {
+            observer = Preconditions.checkNotNull(observer);
+            if (priorityList != null) {
+                priorityList = Preconditions.checkCollectionElementsNotNull(priorityList,
+                        "PrinterId");
+            }
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -338,6 +367,8 @@
 
         @Override
         public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
+            observer = Preconditions.checkNotNull(observer);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -357,6 +388,8 @@
 
         @Override
         public void validatePrinters(List<PrinterId> printerIds, int userId) {
+            printerIds = Preconditions.checkCollectionElementsNotNull(printerIds, "PrinterId");
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -376,6 +409,8 @@
 
         @Override
         public void startPrinterStateTracking(PrinterId printerId, int userId) {
+            printerId = Preconditions.checkNotNull(printerId);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -395,6 +430,8 @@
 
         @Override
         public void stopPrinterStateTracking(PrinterId printerId, int userId) {
+            printerId = Preconditions.checkNotNull(printerId);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -415,6 +452,8 @@
         @Override
         public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
                 int appId, int userId) throws RemoteException {
+            listener = Preconditions.checkNotNull(listener);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final int resolvedAppId;
             final UserState userState;
@@ -437,6 +476,8 @@
         @Override
         public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener,
                 int userId) {
+            listener = Preconditions.checkNotNull(listener);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -456,6 +497,9 @@
 
         @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            fd = Preconditions.checkNotNull(fd);
+            pw = Preconditions.checkNotNull(pw);
+
             if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
                 pw.println("Permission Denial: can't dump PrintManager from from pid="
@@ -707,10 +751,8 @@
             return userId;
         }
 
-        private String resolveCallingPackageNameEnforcingSecurity(String packageName) {
-            if (TextUtils.isEmpty(packageName)) {
-                return null;
-            }
+        private @NonNull String resolveCallingPackageNameEnforcingSecurity(
+                @NonNull String packageName) {
             String[] packages = mContext.getPackageManager().getPackagesForUid(
                     Binder.getCallingUid());
             final int packageCount = packages.length;
@@ -719,7 +761,7 @@
                     return packageName;
                 }
             }
-            return null;
+            throw new IllegalArgumentException("packageName has to belong to the caller");
         }
 
         private int getCurrentUserId () {
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 5ee4066..9b99c67 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -409,7 +409,7 @@
         }
     }
 
-    public void startPrinterStateTracking(PrinterId printerId) {
+    public void startPrinterStateTracking(@NonNull PrinterId printerId) {
         mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING,
                 printerId).sendToTarget();
     }
@@ -420,7 +420,7 @@
      * @param printerId the id of the printer the icon should be loaded for
      * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
      */
-    public void requestCustomPrinterIcon(PrinterId printerId) {
+    public void requestCustomPrinterIcon(@NonNull PrinterId printerId) {
         try {
             if (isBound()) {
                 mPrintService.requestCustomPrinterIcon(printerId);
@@ -430,7 +430,7 @@
         }
     }
 
-    private void handleStartPrinterStateTracking(final PrinterId printerId) {
+    private void handleStartPrinterStateTracking(final @NonNull PrinterId printerId) {
         throwIfDestroyed();
         // Take a note we are tracking the printer.
         if (mTrackedPrinterList == null) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 78edc4d..fcf2fc8 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -20,6 +20,7 @@
 import static android.content.pm.PackageManager.GET_SERVICES;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -176,8 +177,8 @@
     }
 
     @SuppressWarnings("deprecation")
-    public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
-            PrintAttributes attributes, String packageName, int appId) {
+    public Bundle print(@NonNull String printJobName, @NonNull IPrintDocumentAdapter adapter,
+            @Nullable PrintAttributes attributes, @NonNull String packageName, int appId) {
         // Create print job place holder.
         final PrintJobInfo printJob = new PrintJobInfo();
         printJob.setId(new PrintJobId());
@@ -267,7 +268,7 @@
         return new ArrayList<PrintJobInfo>(result.values());
     }
 
-    public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
+    public PrintJobInfo getPrintJobInfo(@NonNull PrintJobId printJobId, int appId) {
         PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId);
         if (printJob == null) {
             printJob = mSpooler.getPrintJobInfo(printJobId, appId);
@@ -290,7 +291,7 @@
      *         not yet available
      * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
      */
-    public Icon getCustomPrinterIcon(PrinterId printerId) {
+    public @Nullable Icon getCustomPrinterIcon(@NonNull PrinterId printerId) {
         Icon icon = mSpooler.getCustomPrinterIcon(printerId);
 
         if (icon == null) {
@@ -303,7 +304,7 @@
         return icon;
     }
 
-    public void cancelPrintJob(PrintJobId printJobId, int appId) {
+    public void cancelPrintJob(@NonNull PrintJobId printJobId, int appId) {
         PrintJobInfo printJobInfo = mSpooler.getPrintJobInfo(printJobId, appId);
         if (printJobInfo == null) {
             return;
@@ -313,15 +314,19 @@
         mSpooler.setPrintJobCancelling(printJobId, true);
 
         if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
-            ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName();
-            RemotePrintService printService = null;
-            synchronized (mLock) {
-                printService = mActiveServices.get(printServiceName);
+            PrinterId printerId = printJobInfo.getPrinterId();
+
+            if (printerId != null) {
+                ComponentName printServiceName = printerId.getServiceName();
+                RemotePrintService printService = null;
+                synchronized (mLock) {
+                    printService = mActiveServices.get(printServiceName);
+                }
+                if (printService == null) {
+                    return;
+                }
+                printService.onRequestCancelPrintJob(printJobInfo);
             }
-            if (printService == null) {
-                return;
-            }
-            printService.onRequestCancelPrintJob(printJobInfo);
         } else {
             // If the print job is failed we do not need cooperation
             // from the print service.
@@ -329,7 +334,7 @@
         }
     }
 
-    public void restartPrintJob(PrintJobId printJobId, int appId) {
+    public void restartPrintJob(@NonNull PrintJobId printJobId, int appId) {
         PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId);
         if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
             return;
@@ -363,7 +368,7 @@
         }
     }
 
-    public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
+    public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
 
@@ -386,7 +391,7 @@
         }
     }
 
-    public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
+    public void destroyPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
         synchronized (mLock) {
             // Already destroyed - nothing to do.
             if (mPrinterDiscoverySession == null) {
@@ -397,8 +402,8 @@
         }
     }
 
-    public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
-            List<PrinterId> printerIds) {
+    public void startPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer,
+            @Nullable List<PrinterId> printerIds) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
             // No services - nothing to do.
@@ -415,7 +420,7 @@
         }
     }
 
-    public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+    public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
             // No services - nothing to do.
@@ -431,7 +436,7 @@
         }
     }
 
-    public void validatePrinters(List<PrinterId> printerIds) {
+    public void validatePrinters(@NonNull List<PrinterId> printerIds) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
             // No services - nothing to do.
@@ -447,7 +452,7 @@
         }
     }
 
-    public void startPrinterStateTracking(PrinterId printerId) {
+    public void startPrinterStateTracking(@NonNull PrinterId printerId) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
             // No services - nothing to do.
@@ -479,7 +484,7 @@
         }
     }
 
-    public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
+    public void addPrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener,
             int appId) throws RemoteException {
         synchronized (mLock) {
             throwIfDestroyedLocked();
@@ -497,7 +502,7 @@
         }
     }
 
-    public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener) {
+    public void removePrintJobStateChangeListener(@NonNull IPrintJobStateChangeListener listener) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
             if (mPrintJobStateChangeListenerRecords == null) {
@@ -613,7 +618,7 @@
         mDestroyed = true;
     }
 
-    public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String prefix) {
         pw.append(prefix).append("user state ").append(String.valueOf(mUserId)).append(":");
         pw.println();
 
@@ -991,10 +996,10 @@
     }
 
     private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient {
-        final IPrintJobStateChangeListener listener;
+        @NonNull final IPrintJobStateChangeListener listener;
         final int appId;
 
-        public PrintJobStateChangeListenerRecord(IPrintJobStateChangeListener listener,
+        public PrintJobStateChangeListenerRecord(@NonNull IPrintJobStateChangeListener listener,
                 int appId) throws RemoteException {
             this.listener = listener;
             this.appId = appId;
@@ -1043,7 +1048,7 @@
                     .sendToTarget();
         }
 
-        public void addObserverLocked(IPrinterDiscoveryObserver observer) {
+        public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
             // Add the observer.
             mDiscoveryObservers.register(observer);
 
@@ -1058,7 +1063,7 @@
             }
         }
 
-        public void removeObserverLocked(IPrinterDiscoveryObserver observer) {
+        public void removeObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
             // Remove the observer.
             mDiscoveryObservers.unregister(observer);
             // No one else observing - then kill it.
@@ -1067,8 +1072,8 @@
             }
         }
 
-        public final void startPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer,
-                List<PrinterId> priorityList) {
+        public final void startPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer,
+                @Nullable List<PrinterId> priorityList) {
             if (mIsDestroyed) {
                 Log.w(LOG_TAG, "Not starting dicovery - session destroyed");
                 return;
@@ -1101,7 +1106,7 @@
                     .sendToTarget();
         }
 
-        public final void stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver observer) {
+        public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) {
             if (mIsDestroyed) {
                 Log.w(LOG_TAG, "Not stopping dicovery - session destroyed");
                 return;
@@ -1121,7 +1126,7 @@
                     .sendToTarget();
         }
 
-        public void validatePrintersLocked(List<PrinterId> printerIds) {
+        public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) {
             if (mIsDestroyed) {
                 Log.w(LOG_TAG, "Not validating pritners - session destroyed");
                 return;
@@ -1135,13 +1140,15 @@
                 ComponentName serviceName = null;
                 while (iterator.hasNext()) {
                     PrinterId printerId = iterator.next();
-                    if (updateList.isEmpty()) {
-                        updateList.add(printerId);
-                        serviceName = printerId.getServiceName();
-                        iterator.remove();
-                    } else if (printerId.getServiceName().equals(serviceName)) {
-                        updateList.add(printerId);
-                        iterator.remove();
+                    if (printerId != null) {
+                        if (updateList.isEmpty()) {
+                            updateList.add(printerId);
+                            serviceName = printerId.getServiceName();
+                            iterator.remove();
+                        } else if (printerId.getServiceName().equals(serviceName)) {
+                            updateList.add(printerId);
+                            iterator.remove();
+                        }
                     }
                 }
                 // Schedule a notification of the service.
@@ -1157,7 +1164,7 @@
             }
         }
 
-        public final void startPrinterStateTrackingLocked(PrinterId printerId) {
+        public final void startPrinterStateTrackingLocked(@NonNull PrinterId printerId) {
             if (mIsDestroyed) {
                 Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed");
                 return;
@@ -1500,8 +1507,8 @@
             service.validatePrinters(printerIds);
         }
 
-        private void handleStartPrinterStateTracking(RemotePrintService service,
-                PrinterId printerId) {
+        private void handleStartPrinterStateTracking(@NonNull RemotePrintService service,
+                @NonNull PrinterId printerId) {
             service.startPrinterStateTracking(printerId);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 536fb70..87569b7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -15,9 +15,6 @@
  */
 package com.android.server.devicepolicy;
 
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-
 import android.Manifest.permission;
 import android.app.Activity;
 import android.app.admin.DeviceAdminReceiver;
@@ -27,21 +24,26 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.net.wifi.WifiInfo;
-import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 import android.util.Pair;
 
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
 import org.mockito.ArgumentCaptor;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -64,13 +66,18 @@
  *
  m FrameworksServicesTests &&
  adb install \
-   -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+   -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
  adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \
    -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
 
  (mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
  */
+@SmallTest
 public class DevicePolicyManagerTest extends DpmTestBase {
+    private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
+            permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
+            permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
+
     private DpmMockContext mContext;
     public DevicePolicyManager dpm;
     public DevicePolicyManagerServiceTestable dpms;
@@ -1543,4 +1550,156 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         assertTrue(dpm.isAffiliatedUser());
     }
+
+    public void testGetUserProvisioningState_defaultResult() {
+        assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
+    }
+
+    public void testSetUserProvisioningState_permission() throws Exception {
+        setupProfileOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+    }
+
+    public void testSetUserProvisioningState_unprivileged() throws Exception {
+        setupProfileOwner();
+        try {
+            dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+                    DpmMockContext.CALLER_USER_HANDLE);
+            fail("Expected SecurityException");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testSetUserProvisioningState_noManagement() {
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        try {
+            dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+                    DpmMockContext.CALLER_USER_HANDLE);
+            fail("IllegalStateException expected");
+        } catch (IllegalStateException e) {
+            MoreAsserts.assertContainsRegex("change provisioning state unless a .* owner is set",
+                    e.getMessage());
+        }
+        assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
+    }
+
+    public void testSetUserProvisioningState_deviceOwnerFromSetupWizard() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
+                DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+    }
+
+    public void testSetUserProvisioningState_deviceOwnerFromSetupWizardAlternative()
+            throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
+                DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+    }
+
+    public void testSetUserProvisioningState_deviceOwnerWithoutSetupWizard() throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        exerciseUserProvisioningTransitions(UserHandle.USER_SYSTEM,
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+    }
+
+    public void testSetUserProvisioningState_managedProfileFromSetupWizard_primaryUser()
+            throws Exception {
+        setupProfileOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+                DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
+                DevicePolicyManager.STATE_USER_UNMANAGED);
+    }
+
+    public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
+            throws Exception {
+        setupProfileOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+                DevicePolicyManager.STATE_USER_SETUP_COMPLETE,
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+    }
+
+    public void testSetUserProvisioningState_managedProfileWithoutSetupWizard() throws Exception {
+        setupProfileOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+                DevicePolicyManager.STATE_USER_SETUP_FINALIZED);
+    }
+
+    public void testSetUserProvisioningState_illegalTransitionOutOfFinalized1() throws Exception {
+        setupProfileOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        try {
+            exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+                    DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+                    DevicePolicyManager.STATE_USER_UNMANAGED);
+            fail("Expected IllegalStateException");
+        } catch (IllegalStateException e) {
+            MoreAsserts.assertContainsRegex("Cannot move to user provisioning state",
+                    e.getMessage());
+        }
+    }
+
+    public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
+            throws Exception {
+        setupProfileOwner();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        try {
+            exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+                    DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
+                    DevicePolicyManager.STATE_USER_SETUP_COMPLETE);
+            fail("Expected IllegalStateException");
+        } catch (IllegalStateException e) {
+            MoreAsserts.assertContainsRegex("Cannot move to user provisioning state",
+                    e.getMessage());
+        }
+    }
+
+    private void exerciseUserProvisioningTransitions(int userId, int... states) {
+        assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
+        for (int state : states) {
+            dpm.setUserProvisioningState(state, userId);
+            assertEquals(state, dpm.getUserProvisioningState());
+        }
+    }
+
+    private void setupProfileOwner() throws Exception {
+        mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);
+
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+        dpm.setActiveAdmin(admin1, false);
+        assertTrue(dpm.setProfileOwner(admin1, null, DpmMockContext.CALLER_USER_HANDLE));
+
+        mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
+    }
+
+    private void setupDeviceOwner() throws Exception {
+        mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);
+
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+        dpm.setActiveAdmin(admin1, false);
+        assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));
+
+        mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index 2640889..31182fc 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -155,16 +155,12 @@
 
     @SmallTest
     public void testTopicImportanceExtractor() throws Exception {
-        mHelper.setTopicImportance("package", 0, new Notification.Topic("A", "a"),
-                IMPORTANCE_MAX);
+        mHelper.setImportance("package", 0, new Notification.Topic("A", "a"), IMPORTANCE_MAX);
         // There is no B. There never was a b. Moving on...
-        mHelper.setTopicImportance("package", 0, new Notification.Topic("C", "c"),
-                IMPORTANCE_HIGH);
-        mHelper.setTopicImportance("package", 0, new Notification.Topic("D", "d"),
-                IMPORTANCE_LOW);
+        mHelper.setImportance("package", 0, new Notification.Topic("C", "c"), IMPORTANCE_HIGH);
+        mHelper.setImportance("package", 0, new Notification.Topic("D", "d"), IMPORTANCE_LOW);
         // watch out: different package.
-        mHelper.setTopicImportance("package2", 0, new Notification.Topic("E", "e"),
-                IMPORTANCE_NONE);
+        mHelper.setImportance("package2", 0, new Notification.Topic("E", "e"), IMPORTANCE_NONE);
 
         TopicImportanceExtractor validator = mHelper.findExtractor(TopicImportanceExtractor.class);
         validator.process(mRecordGroupGSortA);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index aa95e1d..3859294 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -24,7 +24,6 @@
 import java.lang.String;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -210,7 +209,22 @@
          * Call sends responses through connection.
          * @hide
          */
-        public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
+        public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000;
+
+        /**
+         * When set, prevents a video {@code Call} from being downgraded to an audio-only call.
+         * <p>
+         * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
+         * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
+         * downgraded from a video call back to a VideoState of
+         * {@link VideoProfile#STATE_AUDIO_ONLY}.
+         * <p>
+         * Intuitively, a call which can be downgraded to audio should also have local and remote
+         * video
+         * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
+         * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
+         */
+        public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
 
         //******************************************************************************************
         // Next CAPABILITY value: 0x00800000
@@ -332,6 +346,9 @@
             if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
                 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
             }
+            if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
+                builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
+            }
             if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
                 builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
             }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index deb98f4..fa7a59d 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -255,8 +255,23 @@
      */
     public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
 
+    /**
+     * When set, prevents a video call from being downgraded to an audio-only call.
+     * <p>
+     * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
+     * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
+     * downgraded from a video call back to a VideoState of
+     * {@link VideoProfile#STATE_AUDIO_ONLY}.
+     * <p>
+     * Intuitively, a call which can be downgraded to audio should also have local and remote
+     * video
+     * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
+     * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
+     */
+    public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
+
     //**********************************************************************************************
-    // Next CAPABILITY value: 0x00800000
+    // Next CAPABILITY value: 0x01000000
     //**********************************************************************************************
 
     /**
@@ -371,6 +386,9 @@
         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
         }
+        if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
+            builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
+        }
         if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
             builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
         }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 320d274..630dacc 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -746,7 +746,7 @@
      */
     @Nullable
     public PersistableBundle getConfig() {
-        return getConfigForSubId(SubscriptionManager.getDefaultSubId());
+        return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
     }
 
     /**
diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
index 96069213..f71f58d 100644
--- a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
+++ b/telephony/java/android/telephony/DataConnectionRealTimeInfo.java
@@ -28,10 +28,10 @@
 public class DataConnectionRealTimeInfo implements Parcelable {
     private long mTime;             // Time the info was collected since boot in nanos;
 
-    public static int DC_POWER_STATE_LOW       = 1;
-    public static int DC_POWER_STATE_MEDIUM    = 2;
-    public static int DC_POWER_STATE_HIGH      = 3;
-    public static int DC_POWER_STATE_UNKNOWN   = Integer.MAX_VALUE;
+    public static final int DC_POWER_STATE_LOW       = 1;
+    public static final int DC_POWER_STATE_MEDIUM    = 2;
+    public static final int DC_POWER_STATE_HIGH      = 3;
+    public static final int DC_POWER_STATE_UNKNOWN   = Integer.MAX_VALUE;
 
     private int mDcPowerState;      // DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN]
 
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 553221d..b089387 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -2082,7 +2082,7 @@
      * to read the VM number.
      */
     public static boolean isVoiceMailNumber(String number) {
-        return isVoiceMailNumber(SubscriptionManager.getDefaultSubId(), number);
+        return isVoiceMailNumber(SubscriptionManager.getDefaultSubscriptionId(), number);
     }
 
     /**
@@ -2977,7 +2977,7 @@
      * Returns Default voice subscription Id.
      */
     private static int getDefaultVoiceSubId() {
-        return SubscriptionManager.getDefaultVoiceSubId();
+        return SubscriptionManager.getDefaultVoiceSubscriptionId();
     }
     //==== End of utility methods used only in compareStrictly() =====
 }
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 16472c8..ae130d4 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -194,10 +194,12 @@
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
      * READ_PRECISE_PHONE_STATE}
-     *
      * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+     *
+     * @deprecated Use {@link TelephonyManager#getModemActivityInfo()}
      * @hide
      */
+    @Deprecated
     public static final int LISTEN_DATA_CONNECTION_REAL_TIME_INFO           = 0x00002000;
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 9998937..ff8c71c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -53,7 +53,6 @@
     private static final boolean VDBG = false;
 
     /** An invalid subscription identifier */
-    /** @hide */
     public static final int INVALID_SUBSCRIPTION_ID = -1;
 
     /** Base value for Dummy SUBSCRIPTION_ID's. */
@@ -455,8 +454,9 @@
     }
 
     /**
-     * Get the active SubscriptionInfo with the subId key
-     * @param subId The unique SubscriptionInfo key in database
+     * Get the active SubscriptionInfo with the input subId.
+     *
+     * @param subId The unique SubscriptionInfo key in database.
      * @return SubscriptionInfo, maybe null if its not active.
      */
     public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
@@ -898,12 +898,15 @@
     }
 
     /**
-     * @return the "system" defaultSubId on a voice capable device this
-     * will be getDefaultVoiceSubId() and on a data only device it will be
-     * getDefaultDataSubId().
-     * @hide
+     * Returns the system's default subscription id.
+     *
+     * For a voice capable device, it will return getDefaultVoiceSubscriptionId.
+     * For a data only device, it will return the getDefaultDataSubscriptionId.
+     * May return an INVALID_SUBSCRIPTION_ID on error.
+     *
+     * @return the "system" default subscription id.
      */
-    public static int getDefaultSubId() {
+    public static int getDefaultSubscriptionId() {
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
@@ -919,8 +922,14 @@
         return subId;
     }
 
-    /** @hide */
-    public static int getDefaultVoiceSubId() {
+    /**
+     * Returns the system's default voice subscription id.
+     *
+     * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
+     *
+     * @return the default voice subscription Id.
+     */
+    public static int getDefaultVoiceSubscriptionId() {
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
@@ -932,7 +941,7 @@
             // ignore it
         }
 
-        if (VDBG) logd("getDefaultVoiceSubId, sub id = " + subId);
+        if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId);
         return subId;
     }
 
@@ -949,23 +958,31 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Return the SubscriptionInfo for default voice subscription.
+     *
+     * Will return null on data only devices, or on error.
+     *
+     * @return the SubscriptionInfo for the default voice subscription.
+     * @hide
+     */
     public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
-        return getActiveSubscriptionInfo(getDefaultVoiceSubId());
+        return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId());
     }
 
     /** @hide */
     public static int getDefaultVoicePhoneId() {
-        return getPhoneId(getDefaultVoiceSubId());
+        return getPhoneId(getDefaultVoiceSubscriptionId());
     }
 
     /**
-     * @return subId of the DefaultSms subscription or
-     * a value < 0 if an error.
+     * Returns the system's default SMS subscription id.
      *
-     * @hide
+     * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
+     *
+     * @return the default SMS subscription Id.
      */
-    public static int getDefaultSmsSubId() {
+    public static int getDefaultSmsSubscriptionId() {
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
@@ -977,7 +994,7 @@
             // ignore it
         }
 
-        if (VDBG) logd("getDefaultSmsSubId, sub id = " + subId);
+        if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId);
         return subId;
     }
 
@@ -994,18 +1011,31 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Return the SubscriptionInfo for default voice subscription.
+     *
+     * Will return null on data only devices, or on error.
+     *
+     * @return the SubscriptionInfo for the default SMS subscription.
+     * @hide
+     */
     public SubscriptionInfo getDefaultSmsSubscriptionInfo() {
-        return getActiveSubscriptionInfo(getDefaultSmsSubId());
+        return getActiveSubscriptionInfo(getDefaultSmsSubscriptionId());
     }
 
     /** @hide */
     public int getDefaultSmsPhoneId() {
-        return getPhoneId(getDefaultSmsSubId());
+        return getPhoneId(getDefaultSmsSubscriptionId());
     }
 
-    /** @hide */
-    public static int getDefaultDataSubId() {
+    /**
+     * Returns the system's default data subscription id.
+     *
+     * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID.
+     *
+     * @return the default data subscription Id.
+     */
+    public static int getDefaultDataSubscriptionId() {
         int subId = INVALID_SUBSCRIPTION_ID;
 
         try {
@@ -1017,7 +1047,7 @@
             // ignore it
         }
 
-        if (VDBG) logd("getDefaultDataSubId, sub id = " + subId);
+        if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId);
         return subId;
     }
 
@@ -1034,14 +1064,21 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Return the SubscriptionInfo for default data subscription.
+     *
+     * Will return null on voice only devices, or on error.
+     *
+     * @return the SubscriptionInfo for the default data subscription.
+     * @hide
+     */
     public SubscriptionInfo getDefaultDataSubscriptionInfo() {
-        return getActiveSubscriptionInfo(getDefaultDataSubId());
+        return getActiveSubscriptionInfo(getDefaultDataSubscriptionId());
     }
 
     /** @hide */
     public int getDefaultDataPhoneId() {
-        return getPhoneId(getDefaultDataSubId());
+        return getPhoneId(getDefaultDataSubscriptionId());
     }
 
     /** @hide */
@@ -1061,13 +1098,13 @@
     //FIXME this is vulnerable to race conditions
     /** @hide */
     public boolean allDefaultsSelected() {
-        if (!isValidSubscriptionId(getDefaultDataSubId())) {
+        if (!isValidSubscriptionId(getDefaultDataSubscriptionId())) {
             return false;
         }
-        if (!isValidSubscriptionId(getDefaultSmsSubId())) {
+        if (!isValidSubscriptionId(getDefaultSmsSubscriptionId())) {
             return false;
         }
-        if (!isValidSubscriptionId(getDefaultVoiceSubId())) {
+        if (!isValidSubscriptionId(getDefaultVoiceSubscriptionId())) {
             return false;
         }
         return true;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7eaa6a6..fcb42a4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1232,7 +1232,6 @@
      * on a CDMA network).
      * @param subId
      */
-    /** {@hide} */
     public String getNetworkOperatorName(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, "");
@@ -1259,8 +1258,7 @@
      *
      * @param subId
      */
-    /** {@hide} */
-   public String getNetworkOperatorForSubscription(int subId) {
+    public String getNetworkOperator(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getNetworkOperatorForPhone(phoneId);
      }
@@ -1276,7 +1274,7 @@
      * @param phoneId
      * @hide
      **/
-   public String getNetworkOperatorForPhone(int phoneId) {
+    public String getNetworkOperatorForPhone(int phoneId) {
         return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
      }
 
@@ -1298,7 +1296,6 @@
      *
      * @param subId
      */
-    /** {@hide} */
     public boolean isNetworkRoaming(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return Boolean.parseBoolean(getTelephonyProperty(phoneId,
@@ -1327,8 +1324,7 @@
      *
      * @param subId for which Network CountryIso is returned
      */
-    /** {@hide} */
-    public String getNetworkCountryIsoForSubscription(int subId) {
+    public String getNetworkCountryIso(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getNetworkCountryIsoForPhone(phoneId);
     }
@@ -1436,7 +1432,6 @@
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
-    /** {@hide} */
    public int getNetworkType(int subId) {
        try {
            ITelephony telephony = getITelephony();
@@ -1497,7 +1492,6 @@
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
-    /** {@hide} */
     public int getDataNetworkType(int subId) {
         try{
             ITelephony telephony = getITelephony();
@@ -1535,7 +1529,6 @@
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
-    /** {@hide} */
     public int getVoiceNetworkType(int subId) {
         try{
             ITelephony telephony = getITelephony();
@@ -1804,10 +1797,9 @@
      * @see #getSimState
      *
      * @param subId for which SimOperator is returned
-     * @hide
      */
     public String getSimOperator(int subId) {
-        return getSimOperatorNumericForSubscription(subId);
+        return getSimOperatorNumeric(subId);
     }
 
     /**
@@ -1820,17 +1812,17 @@
      * @hide
      */
     public String getSimOperatorNumeric() {
-        int subId = SubscriptionManager.getDefaultDataSubId();
+        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
         if (!SubscriptionManager.isUsableSubIdValue(subId)) {
-            subId = SubscriptionManager.getDefaultSmsSubId();
+            subId = SubscriptionManager.getDefaultSmsSubscriptionId();
             if (!SubscriptionManager.isUsableSubIdValue(subId)) {
-                subId = SubscriptionManager.getDefaultVoiceSubId();
+                subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
                 if (!SubscriptionManager.isUsableSubIdValue(subId)) {
-                    subId = SubscriptionManager.getDefaultSubId();
+                    subId = SubscriptionManager.getDefaultSubscriptionId();
                 }
             }
         }
-        return getSimOperatorNumericForSubscription(subId);
+        return getSimOperatorNumeric(subId);
     }
 
     /**
@@ -1844,7 +1836,7 @@
      * @param subId for which SimOperator is returned
      * @hide
      */
-    public String getSimOperatorNumericForSubscription(int subId) {
+    public String getSimOperatorNumeric(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimOperatorNumericForPhone(phoneId);
     }
@@ -1881,9 +1873,8 @@
      * @see #getSimState
      *
      * @param subId for which SimOperatorName is returned
-     * @hide
      */
-    public String getSimOperatorNameForSubscription(int subId) {
+    public String getSimOperatorName(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimOperatorNameForPhone(phoneId);
     }
@@ -1909,21 +1900,8 @@
      * Returns the ISO country code equivalent for the SIM provider's country code.
      *
      * @param subId for which SimCountryIso is returned
-     *
-     * @hide
      */
     public String getSimCountryIso(int subId) {
-        return getSimCountryIsoForSubscription(subId);
-    }
-
-    /**
-     * Returns the ISO country code equivalent for the SIM provider's country code.
-     *
-     * @param subId for which SimCountryIso is returned
-     *
-     * @hide
-     */
-    public String getSimCountryIsoForSubscription(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimCountryIsoForPhone(phoneId);
     }
@@ -1957,7 +1935,6 @@
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
-    /** {@hide} */
     public String getSimSerialNumber(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -2046,7 +2023,6 @@
      *
      * @param subId whose subscriber id is returned
      */
-    /** {@hide} */
     public String getSubscriberId(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -2089,9 +2065,8 @@
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      *
-     * @param subscription whose subscriber id is returned
+     * @param subId whose subscriber id is returned
      */
-    /** {@hide} */
     public String getGroupIdLevel1(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -2118,7 +2093,7 @@
      * The default SMS app can also use this.
      */
     public String getLine1Number() {
-        return getLine1NumberForSubscriber(getDefaultSubscription());
+        return getLine1Number(getDefaultSubscription());
     }
 
     /**
@@ -2134,8 +2109,7 @@
      *
      * @param subId whose phone number for line 1 is returned
      */
-    /** {@hide} */
-    public String getLine1NumberForSubscriber(int subId) {
+    public String getLine1Number(int subId) {
         String number = null;
         try {
             ITelephony telephony = getITelephony();
@@ -2174,7 +2148,7 @@
      * @return true if the operation was executed correctly.
      */
     public boolean setLine1NumberForDisplay(String alphaTag, String number) {
-        return setLine1NumberForDisplayForSubscriber(getDefaultSubscription(), alphaTag, number);
+        return setLine1NumberForDisplay(getDefaultSubscription(), alphaTag, number);
     }
 
     /**
@@ -2190,9 +2164,8 @@
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
      * @return true if the operation was executed correctly.
-     * @hide
      */
-    public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number) {
+    public boolean setLine1NumberForDisplay(int subId, String alphaTag, String number) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
@@ -2213,7 +2186,7 @@
      * nobody seems to call this.
      */
     public String getLine1AlphaTag() {
-        return getLine1AlphaTagForSubscriber(getDefaultSubscription());
+        return getLine1AlphaTag(getDefaultSubscription());
     }
 
     /**
@@ -2226,8 +2199,7 @@
      * @param subId whose alphabetic identifier associated with line 1 is returned
      * nobody seems to call this.
      */
-    /** {@hide} */
-    public String getLine1AlphaTagForSubscriber(int subId) {
+    public String getLine1AlphaTag(int subId) {
         String alphaTag = null;
         try {
             ITelephony telephony = getITelephony();
@@ -2327,7 +2299,6 @@
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * @param subId whose voice mail number is returned
      */
-    /** {@hide} */
     public String getVoiceMailNumber(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -2400,7 +2371,6 @@
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
      */
-    /** {@hide} */
     public boolean setVoiceMailNumber(int subId, String alphaTag, String number) {
         try {
             ITelephony telephony = getITelephony();
@@ -2466,7 +2436,6 @@
      * @param subId whose alphabetic identifier associated with the
      * voice mail number is returned
      */
-    /** {@hide} */
     public String getVoiceMailAlphaTag(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -2584,7 +2553,6 @@
      *
      * @param subId whose call state is returned
      */
-    /** {@hide} */
     public int getCallState(int subId) {
         try {
             ITelephony telephony = getITelephony();
@@ -3225,19 +3193,19 @@
      * Returns Default subscription.
      */
     private static int getDefaultSubscription() {
-        return SubscriptionManager.getDefaultSubId();
+        return SubscriptionManager.getDefaultSubscriptionId();
     }
 
     /**
      * Returns Default phone.
      */
     private static int getDefaultPhone() {
-        return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubId());
+        return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId());
     }
 
     /** {@hide} */
     public int getDefaultSim() {
-        return SubscriptionManager.getSlotId(SubscriptionManager.getDefaultSubId());
+        return SubscriptionManager.getSlotId(SubscriptionManager.getDefaultSubscriptionId());
     }
 
     /**
@@ -4182,7 +4150,7 @@
     /** @hide */
     @SystemApi
     public void setDataEnabled(boolean enable) {
-        setDataEnabled(SubscriptionManager.getDefaultDataSubId(), enable);
+        setDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId(), enable);
     }
 
     /** @hide */
@@ -4201,7 +4169,7 @@
     /** @hide */
     @SystemApi
     public boolean getDataEnabled() {
-        return getDataEnabled(SubscriptionManager.getDefaultDataSubId());
+        return getDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId());
     }
 
     /** @hide */
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index b9d7297..5f3f773 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -327,7 +327,7 @@
     public static CallerInfo getCallerInfo(Context context, String number) {
         if (VDBG) Rlog.v(TAG, "getCallerInfo() based on number...");
 
-        int subId = SubscriptionManager.getDefaultSubId();
+        int subId = SubscriptionManager.getDefaultSubscriptionId();
         return getCallerInfo(context, number, subId);
     }
 
@@ -444,7 +444,7 @@
     // string in the phone number field.
     /* package */ CallerInfo markAsVoiceMail() {
 
-        int subId = SubscriptionManager.getDefaultSubId();
+        int subId = SubscriptionManager.getDefaultSubscriptionId();
         return markAsVoiceMail(subId);
 
     }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index c754068..05cb31e 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -387,7 +387,7 @@
     public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
             OnQueryCompleteListener listener, Object cookie) {
 
-        int subId = SubscriptionManager.getDefaultSubId();
+        int subId = SubscriptionManager.getDefaultSubscriptionId();
         return startQuery(token, context, number, listener, cookie, subId);
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 76b69ce..907d76e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -21,7 +21,6 @@
 import android.net.NetworkCapabilities;
 import android.os.Bundle;
 import android.telephony.CellInfo;
-import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.CellInfo;
@@ -65,7 +64,6 @@
     void notifyPreciseDataConnectionFailed(String reason, String apnType, String apn,
             String failCause);
     void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
-    void notifyDataConnectionRealTimeInfo(in DataConnectionRealTimeInfo dcRtInfo);
     void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
     void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
     void notifySubscriptionInfoChanged();
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
index 0c36063..4c12c2d 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -16,8 +16,9 @@
 
 package android.security.net.config;
 
-import java.util.Set;
+import android.util.ArraySet;
 import java.security.cert.X509Certificate;
+import java.util.Set;
 
 import com.android.org.conscrypt.TrustedCertificateIndex;
 
@@ -33,10 +34,12 @@
         }
     }
 
+    @Override
     public Set<X509Certificate> getCertificates() {
             return mCertificates;
     }
 
+    @Override
     public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) {
         java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert);
         if (anchor == null) {
@@ -45,6 +48,7 @@
         return anchor.getTrustedCert();
     }
 
+    @Override
     public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
         java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
         if (anchor == null) {
@@ -52,4 +56,13 @@
         }
         return anchor.getTrustedCert();
     }
+
+    @Override
+    public Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert) {
+        Set<X509Certificate> certs = new ArraySet<X509Certificate>();
+        for (java.security.cert.TrustAnchor anchor : mIndex.findAllByIssuerAndSignature(cert)) {
+            certs.add(anchor.getTrustedCert());
+        }
+        return certs;
+    }
 }
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index f3a9ea3..3ecb2c4 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -907,6 +907,8 @@
     Maybe<std::string> versionCode, versionName;
     Maybe<std::string> customJavaPackage;
     std::vector<std::string> extraJavaPackages;
+    bool legacyXFlag = false;
+    bool requireLocalization = false;
     Flags flags = Flags()
             .requiredFlag("-o", "Output path", &options.outputPath)
             .requiredFlag("--manifest", "Path to the Android manifest to build",
@@ -922,6 +924,10 @@
             .optionalSwitch("--no-auto-version",
                             "Disables automatic style and layout SDK versioning",
                             &options.noAutoVersion)
+            .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
+                            &legacyXFlag)
+            .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
+                            &requireLocalization)
             .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
                             "by -o",
                             &options.outputToDirectory)
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 48012db..a3f3821 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -87,6 +87,11 @@
     }
 
     @Override
+    public void updatePointerIcon(float x, float y) {
+        // pass for now
+    }
+
+    @Override
     public void dispatchSystemUiVisibilityChanged(int seq, int globalUi,
             int localValue, int localChanges) {
         // pass for now.
@@ -105,4 +110,5 @@
         // pass for now.
         return null;
     }
+
 }
diff --git a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
deleted file mode 100644
index 50bec33..0000000
--- a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.net.wifi.ScanResult;
-import android.net.wifi.passpoint.WifiPasspointPolicy;
-import android.net.wifi.passpoint.WifiPasspointCredential;
-import android.os.Messenger;
-
-/**
- * Interface that allows controlling and querying Wifi Passpoint connectivity.
- *
- * {@hide}
- */
-interface IWifiPasspointManager
-{
-    Messenger getMessenger();
-
-    int getPasspointState();
-
-    List<WifiPasspointPolicy> requestCredentialMatch(in List<ScanResult> requested);
-
-    List<WifiPasspointCredential> getCredentials();
-
-    boolean addCredential(in WifiPasspointCredential cred);
-
-    boolean updateCredential(in WifiPasspointCredential cred);
-
-    boolean removeCredential(in WifiPasspointCredential cred);
-}
-
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl
deleted file mode 100644
index cfd3605..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointCredential;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
deleted file mode 100644
index a100aed..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ /dev/null
@@ -1,665 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.net.wifi.WifiEnterpriseConfig;
-import android.os.Parcelable;
-import android.os.Parcel;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-
-/**
- * A class representing a Wi-Fi Passpoint credential.
- * @hide
- */
-public class WifiPasspointCredential implements Parcelable {
-
-    private final static String TAG = "PasspointCredential";
-    private final static boolean DBG = true;
-
-    /** Wi-Fi nodes**/
-    private String mWifiSpFqdn;
-
-    /** PerProviderSubscription nodes **/
-    private String mCredentialName;
-
-    /** SubscriptionUpdate nodes **/
-    private String mSubscriptionUpdateInterval;
-    private String mSubscriptionUpdateMethod;
-    private String mSubscriptionUpdateRestriction;
-    private String mSubscriptionUpdateURI;
-    private String mSubscriptionUpdateUsername;
-    private String mSubscriptionUpdatePassword;
-
-    /** HomeSP nodes **/
-    private String mHomeSpFqdn;
-    private String mFriendlyName;
-    private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList;
-    private Collection<WifiPasspointDmTree.OtherHomePartners> mOtherHomePartnerList;
-
-    /** SubscriptionParameters nodes**/
-    private String mCreationDate;
-    private String mExpirationDate;
-
-    /** Credential nodes **/
-    private String mType;
-    private String mInnerMethod;
-    private String mCertType;
-    private String mCertSha256Fingerprint;
-    private String mUpdateIdentifier;
-    private String mUsername;
-    private String mPasswd;
-    private String mRealm;
-    private String mImsi;
-    private String mMcc;
-    private String mMnc;
-    private String mCaRootCert;
-    private String mClientCert;
-    private boolean mCheckAaaServerCertStatus;
-
-    /** Policy nodes **/
-    private String mPolicyUpdateUri;
-    private String mPolicyUpdateInterval;
-    private String mPolicyUpdateUsername;
-    private String mPolicyUpdatePassword;
-    private String mPolicyUpdateRestriction;
-    private String mPolicyUpdateMethod;
-    private Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> mPreferredRoamingPartnerList;
-    private Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> mMinBackhaulThresholdNetwork;
-    private Collection<WifiPasspointDmTree.SPExclusionList> mSpExclusionList;
-    private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple;
-    private String mMaxBssLoad;
-
-    /** CrednetialPriority node **/
-    private int mCrednetialPriority;
-
-    /** AAAServerTrustRoot nodes **/
-    private String mAaaCertUrl;
-    private String mAaaSha256Fingerprint;
-
-    /** Others **/
-    private boolean mIsMachineRemediation;
-    private boolean mUserPreferred = false;
-    private String mWifiTreePath;
-    private WifiEnterpriseConfig mEnterpriseConfig;
-
-    /** @hide */
-    public WifiPasspointCredential() {}
-
-    /**
-     * Constructor
-     * @param realm Realm of the passpoint credential
-     * @param fqdn Fully qualified domain name (FQDN) of the credential
-     * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
-     * @see WifiEnterpriseConfig
-     */
-    public WifiPasspointCredential(String realm, String fqdn, WifiEnterpriseConfig config) {
-        mRealm = realm;
-        switch (config.getEapMethod()) {
-            case WifiEnterpriseConfig.Eap.TLS:
-            case WifiEnterpriseConfig.Eap.TTLS:
-                mEnterpriseConfig = new WifiEnterpriseConfig(config);
-                break;
-            default:
-                // ignore
-        }
-    }
-
-    /** @hide */
-    public WifiPasspointCredential(String type,
-            String caroot,
-            String clientcert,
-            String mcc,
-            String mnc,
-            WifiPasspointDmTree.SpFqdn sp,
-            WifiPasspointDmTree.CredentialInfo credinfo) {
-
-        if (credinfo == null) {
-            return;
-        }
-
-        mType = type;
-        mCaRootCert = caroot;
-        mClientCert = clientcert;
-
-        mWifiSpFqdn = sp.nodeName;
-        mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier;
-
-        mCredentialName = credinfo.nodeName;
-        mOtherHomePartnerList = credinfo.homeSP.otherHomePartners.values();
-
-        Set set = credinfo.aAAServerTrustRoot.entrySet();
-        Iterator i = set.iterator();
-        if (i.hasNext()) {
-            Map.Entry entry3 = (Map.Entry) i.next();
-            WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue();
-            mAaaCertUrl = aaa.CertURL;
-            mAaaSha256Fingerprint = aaa.CertSHA256Fingerprint;
-        }
-
-        mCertType = credinfo.credential.digitalCertificate.CertificateType;
-        mCertSha256Fingerprint = credinfo.credential.digitalCertificate.CertSHA256Fingerprint;
-        mUsername = credinfo.credential.usernamePassword.Username;
-        mPasswd = credinfo.credential.usernamePassword.Password;
-        mIsMachineRemediation = credinfo.credential.usernamePassword.MachineManaged;
-        mInnerMethod = credinfo.credential.usernamePassword.eAPMethod.InnerMethod;
-        mImsi = credinfo.credential.sim.IMSI;
-        mMcc = mcc;
-        mMnc = mnc;
-        mCreationDate = credinfo.credential.CreationDate;
-        mExpirationDate = credinfo.credential.ExpirationDate;
-        mRealm = credinfo.credential.Realm;
-
-        if (credinfo.credentialPriority == null) {
-            mCrednetialPriority = 128;
-        } else {
-            mCrednetialPriority = Integer.parseInt(credinfo.credentialPriority);
-        }
-
-        mHomeSpFqdn = credinfo.homeSP.FQDN;
-
-        mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval;
-        mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod;
-        mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction;
-        mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI;
-        mSubscriptionUpdateUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
-        mSubscriptionUpdatePassword = credinfo.subscriptionUpdate.usernamePassword.Password;
-
-        mPolicyUpdateUri = credinfo.policy.policyUpdate.URI;
-        mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval;
-        mPolicyUpdateUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
-        mPolicyUpdatePassword = credinfo.policy.policyUpdate.usernamePassword.Password;
-        mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction;
-        mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod;
-        mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values();
-        mMinBackhaulThresholdNetwork = credinfo.policy.minBackhaulThreshold.values();
-        mRequiredProtoPortTuple = credinfo.policy.requiredProtoPortTuple.values();
-        mMaxBssLoad = credinfo.policy.maximumBSSLoadValue;
-        mSpExclusionList = credinfo.policy.sPExclusionList.values();
-
-        mHomeOIList = credinfo.homeSP.homeOIList.values();
-        mFriendlyName = credinfo.homeSP.FriendlyName;
-        mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus;
-    }
-
-    /** @hide */
-    public String getUpdateIdentifier() {
-        return mUpdateIdentifier;
-    }
-
-    /** @hide */
-    public String getUpdateMethod() {
-        return mSubscriptionUpdateMethod;
-    }
-
-    /** @hide */
-    public void setUpdateMethod(String method) {
-        mSubscriptionUpdateMethod = method;
-    }
-
-    /** @hide */
-    public String getWifiSpFqdn() {
-        return mWifiSpFqdn;
-    }
-
-    /** @hide */
-    public String getCredName() {
-        return mCredentialName;
-    }
-
-    /** @hide */
-    public String getType() {
-        return mType;
-    }
-
-    /**
-     * Get enterprise config of this Passpoint credential.
-     * @return Enterprise config
-     * @see WifiEnterpriseConfig
-     */
-    public WifiEnterpriseConfig getEnterpriseConfig() {
-        return new WifiEnterpriseConfig(mEnterpriseConfig);
-    }
-
-    /**
-     * Set enterprise config of this Passpoint credential.
-     * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
-     * @see WifiEnterpriseConfig
-     */
-    public void setEnterpriseConfig(WifiEnterpriseConfig config) {
-        // TODO
-    }
-
-    /** @hide */
-    public String getCertType() {
-        return mCertType;
-    }
-
-    /** @hide */
-    public String getCertSha256Fingerprint() {
-        return mCertSha256Fingerprint;
-    }
-
-    /** @hide */
-    public String getUserName() {
-        return mUsername;
-    }
-
-    /** @hide */
-    public String getPassword() {
-        // TODO: guarded by connectivity internal
-        return mPasswd;
-    }
-
-    /** @hide */
-    public String getImsi() {
-        return mImsi;
-    }
-
-    /** @hide */
-    public String getMcc() {
-        return mMcc;
-    }
-
-    /** @hide */
-    public String getMnc() {
-        return mMnc;
-    }
-
-    /** @hide */
-    public String getCaRootCertPath() {
-        return mCaRootCert;
-    }
-
-    /** @hide */
-    public String getClientCertPath() {
-        return mClientCert;
-    }
-
-    /**
-     * Get the realm of this Passpoint credential.
-     * @return Realm
-     */
-    public String getRealm() {
-        return mRealm;
-    }
-
-    /**
-     * Set the ream of this Passpoint credential.
-     * @param realm Realm
-     */
-    public void setRealm(String realm) {
-        mRealm = realm;
-    }
-
-    /** @hide */
-    public int getPriority() {
-        if (mUserPreferred) {
-            return 0;
-        }
-
-        return mCrednetialPriority;
-    }
-
-    /**
-     * Get the fully qualified domain name (FQDN) of this Passpoint credential.
-     * @return FQDN
-     */
-    public String getHomeSpFqdn() {
-        return mHomeSpFqdn;
-    }
-
-    /**
-     * Set the fully qualified domain name (FQDN) of this Passpoint credential.
-     * @param fqdn FQDN
-     */
-    public void setHomeFqdn(String fqdn) {
-        mHomeSpFqdn = fqdn;
-    }
-
-
-    /** @hide */
-    public Collection<WifiPasspointDmTree.OtherHomePartners> getOtherHomePartnerList() {
-        return mOtherHomePartnerList;
-    }
-
-    /** @hide */
-    public String getSubscriptionUpdateUsername() {
-        return mSubscriptionUpdateUsername;
-    }
-
-    /** @hide */
-    public String getSubscriptionUpdatePassword() {
-        return mSubscriptionUpdatePassword;
-    }
-
-    /** @hide */
-    public String getPolicyUpdateUri() {
-        return mPolicyUpdateUri;
-    }
-
-    /** @hide */
-    public String getPolicyUpdateInterval() {
-        return mPolicyUpdateInterval;
-    }
-
-    /** @hide */
-    public String getPolicyUpdateUsername() {
-        return mPolicyUpdateUsername;
-    }
-
-    /** @hide */
-    public String getPolicyUpdatePassword() {
-        return mPolicyUpdatePassword;
-    }
-
-    /** @hide */
-    public String getPolicyUpdateRestriction() {
-        return mPolicyUpdateRestriction;
-    }
-
-    /** @hide */
-    public String getPolicyUpdateMethod() {
-        return mPolicyUpdateMethod;
-    }
-
-    /** @hide */
-    public String getCreationDate() {
-        return mCreationDate;
-    }
-
-    /** @hide */
-    public String getExpirationDate() {
-        return mExpirationDate;
-    }
-
-    /** @hide */
-    public void setExpirationDate(String expirationdate) {
-        mExpirationDate = expirationdate;
-    }
-
-    /** @hide */
-    public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPreferredRoamingPartnerList() {
-        return mPreferredRoamingPartnerList;
-    }
-
-    /** @hide */
-    public Collection<WifiPasspointDmTree.HomeOIList> getHomeOiList() {
-        return mHomeOIList;
-    }
-
-    /** @hide */
-    public Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> getBackhaulThresholdList() {
-        return mMinBackhaulThresholdNetwork;
-    }
-
-    /** @hide */
-    public Collection<WifiPasspointDmTree.RequiredProtoPortTuple> getRequiredProtoPortList() {
-        return mRequiredProtoPortTuple;
-    }
-
-    /** @hide */
-    public Collection<WifiPasspointDmTree.SPExclusionList> getSPExclusionList() {
-        return mSpExclusionList;
-    }
-
-    /** @hide */
-    public boolean getIsMachineRemediation() {
-        return mIsMachineRemediation;
-    }
-
-    /** @hide */
-    public String getAaaCertUrl() {
-        return mAaaCertUrl;
-    }
-
-    /** @hide */
-    public String getAaaSha256Fingerprint() {
-        return mAaaSha256Fingerprint;
-    }
-
-    /** @hide */
-    public String getSubscriptionUpdateRestriction() {
-        return mSubscriptionUpdateRestriction;
-    }
-
-    /** @hide */
-    public String getSubscriptionUpdateURI() {
-        return mSubscriptionUpdateURI;
-    }
-
-    /** @hide */
-    public String getSubscriptionUpdateInterval() {
-        return mSubscriptionUpdateInterval;
-    }
-
-    /** @hide */
-    public String getFriendlyName() {
-        return mFriendlyName;
-    }
-
-    /** @hide */
-    public String getMaxBssLoad() {
-        return mMaxBssLoad;
-    }
-
-    /** @hide */
-    public boolean getUserPreference() {
-        return mUserPreferred;
-    }
-
-    /** @hide */
-    public boolean getCheckAaaServerCertStatus() {
-        return mCheckAaaServerCertStatus;
-    }
-
-    /** @hide */
-    public void setUserPreference(boolean value) {
-        mUserPreferred = value;
-    }
-
-    @Override
-    /** @hide */
-    public boolean equals(Object obj) {
-        boolean result = false;
-        if (obj instanceof WifiPasspointCredential) {
-            final WifiPasspointCredential other = (WifiPasspointCredential) obj;
-            if (this.mType.equals(other.mType)) {
-                if (this.mType.equals("TTLS")) {
-                    result = this.mUsername.equals(other.mUsername) &&
-                            this.mPasswd.equals(other.mPasswd) &&
-                            this.mRealm.equals(other.mRealm) &&
-                            this.mHomeSpFqdn.equals(other.mHomeSpFqdn);
-                }
-                if (this.mType.equals("TLS")) {
-                    result = this.mRealm.equals(other.mRealm) &&
-                            this.mHomeSpFqdn.equals(other.mHomeSpFqdn) &&
-                            this.mClientCert.equals(other.mClientCert);
-                }
-                if (this.mType.equals("SIM")) {
-                    result = this.mMcc.equals(other.mMcc) &&
-                            this.mMnc.equals(other.mMnc) &&
-                            this.mImsi.equals(other.mImsi) &&
-                            this.mHomeSpFqdn.equals(other.mHomeSpFqdn);
-                }
-            }
-        }
-        return result;
-    }
-
-    @Override
-    /** @hide */
-    public String toString() {
-        StringBuffer sb = new StringBuffer();
-        String none = "<none>";
-
-        if (!DBG) {
-            sb.append(none);
-        } else {
-            sb.append(", UpdateIdentifier: ")
-            .append(mUpdateIdentifier == null ? none : mUpdateIdentifier)
-            .append(", SubscriptionUpdateMethod: ")
-            .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod)
-            .append(", Type: ").append(mType == null ? none : mType)
-            .append(", Username: ").append(mUsername == null ? none : mUsername)
-            .append(", Passwd: ").append(mPasswd == null ? none : mPasswd)
-            .append(", SubDMAccUsername: ")
-            .append(mSubscriptionUpdateUsername == null ? none : mSubscriptionUpdateUsername)
-            .append(", SubDMAccPassword: ")
-            .append(mSubscriptionUpdatePassword == null ? none : mSubscriptionUpdatePassword)
-            .append(", PolDMAccUsername: ")
-            .append(mPolicyUpdateUsername == null ? none : mPolicyUpdateUsername)
-            .append(", PolDMAccPassword: ")
-            .append(mPolicyUpdatePassword == null ? none : mPolicyUpdatePassword)
-            .append(", Imsi: ").append(mImsi == null ? none : mImsi)
-            .append(", Mcc: ").append(mMcc == null ? none : mMcc)
-            .append(", Mnc: ").append(mMnc == null ? none : mMnc)
-            .append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert)
-            .append(", Realm: ").append(mRealm == null ? none : mRealm)
-            .append(", Priority: ").append(mCrednetialPriority)
-            .append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn)
-            .append(", Otherhomepartners: ")
-            .append(mOtherHomePartnerList == null ? none : mOtherHomePartnerList)
-            .append(", ExpirationDate: ")
-            .append(mExpirationDate == null ? none : mExpirationDate)
-            .append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad)
-            .append(", SPExclusionList: ").append(mSpExclusionList);
-
-            if (mPreferredRoamingPartnerList != null) {
-                sb.append("PreferredRoamingPartnerList:");
-                for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) {
-                    sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match).
-                            append(", priority:").append(prpListItem.Priority).
-                            append(", country:").append(prpListItem.Country).append("]");
-                }
-            }
-
-            if (mHomeOIList != null) {
-                sb.append("HomeOIList:");
-                for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) {
-                    sb.append("[HomeOI:").append(HomeOIListItem.HomeOI).
-                            append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired).
-                            append("]");
-                }
-            }
-
-            if (mMinBackhaulThresholdNetwork != null) {
-                sb.append("BackHaulThreshold:");
-                for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) {
-                    sb.append("[networkType:").append(BhtListItem.NetworkType).
-                            append(", dlBandwidth:").append(BhtListItem.DLBandwidth).
-                            append(", ulBandwidth:").append(BhtListItem.ULBandwidth).
-                            append("]");
-                }
-            }
-
-            if (mRequiredProtoPortTuple != null) {
-                sb.append("WifiMORequiredProtoPortTupleList:");
-                for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) {
-                    sb.append("[IPProtocol:").append(RpptListItem.IPProtocol).
-                            append(", PortNumber:").append(RpptListItem.PortNumber).
-                            append("]");
-                }
-            }
-        }
-        return sb.toString();
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public int describeContents() {
-        return 0;
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mWifiSpFqdn);
-        dest.writeString(mCredentialName);
-        dest.writeString(mType);
-        dest.writeInt(mCrednetialPriority);
-        dest.writeString(mHomeSpFqdn);
-        dest.writeString(mRealm);
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public void readFromParcel(Parcel in) {
-        mWifiSpFqdn = in.readString();
-        mCredentialName = in.readString();
-        mType = in.readString();
-        mCrednetialPriority = in.readInt();
-        mHomeSpFqdn = in.readString();
-        mRealm = in.readString();
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public static final Creator<WifiPasspointCredential> CREATOR =
-            new Creator<WifiPasspointCredential>() {
-                public WifiPasspointCredential createFromParcel(Parcel in) {
-                    WifiPasspointCredential pc = new WifiPasspointCredential();
-                    pc.mWifiSpFqdn = in.readString();
-                    pc.mCredentialName = in.readString();
-                    pc.mType = in.readString();
-                    pc.mCrednetialPriority = in.readInt();
-                    pc.mHomeSpFqdn = in.readString();
-                    pc.mRealm = in.readString();
-                    return pc;
-                }
-
-                public WifiPasspointCredential[] newArray(int size) {
-                    return new WifiPasspointCredential[size];
-                }
-            };
-
-    /** @hide */
-    public int compareTo(WifiPasspointCredential another) {
-
-        //The smaller the higher
-        if (mCrednetialPriority < another.mCrednetialPriority) {
-            return -1;
-        } else if (mCrednetialPriority == another.mCrednetialPriority) {
-            return this.mType.compareTo(another.mType);
-        } else {
-            return 1;
-        }
-    }
-
-    @Override
-    /** @hide */
-    public int hashCode() {
-        int hash = 208;
-        if (mType != null) {
-            hash += mType.hashCode();
-        }
-        if (mRealm != null) {
-            hash += mRealm.hashCode();
-        }
-        if (mHomeSpFqdn != null) {
-            hash += mHomeSpFqdn.hashCode();
-        }
-        if (mUsername != null) {
-            hash += mUsername.hashCode();
-        }
-        if (mPasswd != null) {
-            hash += mPasswd.hashCode();
-        }
-
-        return hash;
-    }
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl
deleted file mode 100644
index 6a88b2e..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointDmTree;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
deleted file mode 100644
index 427c84c..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
+++ /dev/null
@@ -1,1375 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.os.Parcelable;
-import android.os.Parcel;
-import java.util.HashMap;
-
-/**
- * Required Mobile Device Management Tree Structure
- *
- *                   +----------+
- *                   | ./(Root) |
- *                   +----+-----+
- *                        |
- *  +---------+           |         +---------+  +---------+
- *  | DevInfo |-----------+---------|  Wi-Fi  |--|SP FQDN* |
- *  +---------+           |         +---------+  +---------+
- *  +---------+           |                           |
- *  |DevDetail|-----------+                      +-----------------------+
- *  +---------+                                  |PerproviderSubscription|--<X>+
- *                                               +-----------------------+
- *
- * This class contains all nodes start from Wi-Fi
- * @hide
- **/
-public class WifiPasspointDmTree implements Parcelable {
-    private final static String TAG = "WifiTree";
-    public int PpsMoId;//plugfest used only
-    public HashMap<String, SpFqdn> spFqdn = new HashMap<String, SpFqdn>();//Maps.newHashMap();
-
-    public SpFqdn createSpFqdn(String name) {
-        SpFqdn obj = new SpFqdn(name);
-        spFqdn.put(name, obj);
-        return obj;
-    }
-
-    public static class SpFqdn implements Parcelable {
-        public String nodeName;
-        public PerProviderSubscription perProviderSubscription = new PerProviderSubscription();
-
-        public SpFqdn(String name) {
-            nodeName = name;
-        }
-
-        public SpFqdn() {
-        }
-
-        public SpFqdn(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeParcelable(perProviderSubscription, flags);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                perProviderSubscription = in.readParcelable(PerProviderSubscription.class
-                        .getClassLoader());
-            }
-        }
-
-        public static final Parcelable.Creator<SpFqdn> CREATOR = new Parcelable.Creator<SpFqdn>() {
-            public SpFqdn createFromParcel(Parcel in) {
-                return new SpFqdn(in);
-            }
-
-            public SpFqdn[] newArray(int size) {
-                return new SpFqdn[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription
-     **/
-    public static class PerProviderSubscription implements Parcelable {
-        /**
-         * PerProviderSubscription/UpdateIdentifier
-         **/
-        public String UpdateIdentifier;
-        public HashMap<String, CredentialInfo> credentialInfo = new HashMap<String, CredentialInfo>();
-
-        public CredentialInfo createCredentialInfo(String name) {
-            CredentialInfo obj = new CredentialInfo(name);
-            credentialInfo.put(name, obj);
-            return obj;
-        }
-
-        public PerProviderSubscription() {
-        }
-
-        public PerProviderSubscription(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(UpdateIdentifier);
-            out.writeMap(credentialInfo);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                UpdateIdentifier = in.readString();
-                in.readMap(credentialInfo, CredentialInfo.class.getClassLoader());
-            }
-        }
-
-        public static final Parcelable.Creator<PerProviderSubscription> CREATOR = new Parcelable.Creator<PerProviderSubscription>() {
-            public PerProviderSubscription createFromParcel(Parcel in) {
-                return new PerProviderSubscription(in);
-            }
-
-            public PerProviderSubscription[] newArray(int size) {
-                return new PerProviderSubscription[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>
-     * This interior node contains the Home SP information, subscription policy, management and credential information.
-     **/
-    public static class CredentialInfo implements Parcelable {
-        public String nodeName;
-        public Policy policy = new Policy();
-        public String credentialPriority;
-        public HashMap<String, AAAServerTrustRoot> aAAServerTrustRoot = new HashMap<String, AAAServerTrustRoot>();
-        public SubscriptionUpdate subscriptionUpdate = new SubscriptionUpdate();
-        public HomeSP homeSP = new HomeSP();
-        public SubscriptionParameters subscriptionParameters = new SubscriptionParameters();
-        public Credential credential = new Credential();
-        public Extension extension = new Extension();
-
-        public CredentialInfo(String nn) {
-            nodeName = nn;
-        }
-
-        public AAAServerTrustRoot createAAAServerTrustRoot(String name, String url, String fp) {
-            AAAServerTrustRoot obj = new AAAServerTrustRoot(name, url, fp);
-            aAAServerTrustRoot.put(name, obj);
-            return obj;
-        }
-
-        public CredentialInfo() {
-        }
-
-        public CredentialInfo(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeParcelable(policy, flags);
-            out.writeString(credentialPriority);
-            out.writeMap(aAAServerTrustRoot);
-            out.writeParcelable(subscriptionUpdate, flags);
-            out.writeParcelable(homeSP, flags);
-            out.writeParcelable(subscriptionParameters, flags);
-            out.writeParcelable(credential, flags);
-            //out.writeParcelable(extension, flags);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                policy = in.readParcelable(Policy.class.getClassLoader());
-                credentialPriority = in.readString();
-                in.readMap(aAAServerTrustRoot, AAAServerTrustRoot.class.getClassLoader());
-                subscriptionUpdate = in.readParcelable(SubscriptionUpdate.class.getClassLoader());
-                homeSP = in.readParcelable(HomeSP.class.getClassLoader());
-                subscriptionParameters = in.readParcelable(SubscriptionParameters.class
-                        .getClassLoader());
-                credential = in.readParcelable(Credential.class.getClassLoader());
-                //extension = in.readParcelable(Extension.class.getClassLoader());
-            }
-        }
-
-        public static final Parcelable.Creator<CredentialInfo> CREATOR = new Parcelable.Creator<CredentialInfo>() {
-            public CredentialInfo createFromParcel(Parcel in) {
-                return new CredentialInfo(in);
-            }
-
-            public CredentialInfo[] newArray(int size) {
-                return new CredentialInfo[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Policy
-     **/
-    public static class Policy implements Parcelable {
-        public HashMap<String, PreferredRoamingPartnerList> preferredRoamingPartnerList = new HashMap<String, PreferredRoamingPartnerList>();
-        public HashMap<String, MinBackhaulThresholdNetwork> minBackhaulThreshold = new HashMap<String, MinBackhaulThresholdNetwork>();
-        public PolicyUpdate policyUpdate = new PolicyUpdate();
-        public HashMap<String, SPExclusionList> sPExclusionList = new HashMap<String, SPExclusionList>();
-        public HashMap<String, RequiredProtoPortTuple> requiredProtoPortTuple = new HashMap<String, RequiredProtoPortTuple>();
-        public String maximumBSSLoadValue;
-
-        public PreferredRoamingPartnerList createPreferredRoamingPartnerList(String name,
-                String fqdn, String priority, String country) {
-            PreferredRoamingPartnerList obj = new PreferredRoamingPartnerList(name, fqdn, priority,
-                    country);
-            preferredRoamingPartnerList.put(name, obj);
-            return obj;
-        }
-
-        public MinBackhaulThresholdNetwork createMinBackhaulThreshold(String name, String type,
-                String dl, String ul) {
-            MinBackhaulThresholdNetwork obj = new MinBackhaulThresholdNetwork(name, type, dl, ul);
-            minBackhaulThreshold.put(name, obj);
-            return obj;
-        }
-
-        public SPExclusionList createSPExclusionList(String name, String ssid) {
-            SPExclusionList obj = new SPExclusionList(name, ssid);
-            sPExclusionList.put(name, obj);
-            return obj;
-        }
-
-        public RequiredProtoPortTuple createRequiredProtoPortTuple(String name, String proto,
-                String port) {
-            RequiredProtoPortTuple obj = new RequiredProtoPortTuple(name, proto, port);
-            requiredProtoPortTuple.put(name, obj);
-            return obj;
-        }
-
-        public Policy() {
-        }
-
-        public Policy(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeMap(preferredRoamingPartnerList);
-            out.writeMap(minBackhaulThreshold);
-            out.writeParcelable(policyUpdate, flags);
-            out.writeMap(sPExclusionList);
-            out.writeMap(requiredProtoPortTuple);
-            out.writeString(maximumBSSLoadValue);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                in.readMap(preferredRoamingPartnerList,
-                        PreferredRoamingPartnerList.class.getClassLoader());
-                in.readMap(minBackhaulThreshold, MinBackhaulThresholdNetwork.class.getClassLoader());
-                policyUpdate = in.readParcelable(PolicyUpdate.class.getClassLoader());
-                in.readMap(sPExclusionList, SPExclusionList.class.getClassLoader());
-                in.readMap(requiredProtoPortTuple, RequiredProtoPortTuple.class.getClassLoader());
-                maximumBSSLoadValue = in.readString();
-
-            }
-        }
-
-        public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() {
-            public Policy createFromParcel(Parcel in) {
-                return new Policy(in);
-            }
-
-            public Policy[] newArray(int size) {
-                return new Policy[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>
-     **/
-    public static class PreferredRoamingPartnerList implements Parcelable {
-        public String nodeName;
-        public String FQDN_Match; //maximum 255 + ",includeSubdomains", equals 273
-        public String Priority;
-        public String Country; // maximum 600 octets
-
-        public PreferredRoamingPartnerList(String nn, String f, String p, String c) {
-            nodeName = nn;
-            FQDN_Match = f;
-            Priority = p;
-            Country = c;
-        }
-
-        public PreferredRoamingPartnerList() {
-        }
-
-        public PreferredRoamingPartnerList(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeString(FQDN_Match);
-            out.writeString(Priority);
-            out.writeString(Country);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                FQDN_Match = in.readString();
-                Priority = in.readString();
-                Country = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<PreferredRoamingPartnerList> CREATOR = new Parcelable.Creator<PreferredRoamingPartnerList>() {
-            public PreferredRoamingPartnerList createFromParcel(Parcel in) {
-                return new PreferredRoamingPartnerList(in);
-            }
-
-            public PreferredRoamingPartnerList[] newArray(int size) {
-                return new PreferredRoamingPartnerList[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Policy/MinBackhaulThreshold
-     **/
-    public static class MinBackhaulThresholdNetwork implements Parcelable {
-        public String nodeName;
-        public String NetworkType;
-        public String DLBandwidth;
-        public String ULBandwidth;
-
-        public MinBackhaulThresholdNetwork(String nn, String nt, String d, String u) {
-            nodeName = nn;
-            NetworkType = nt;
-            DLBandwidth = d;
-            ULBandwidth = u;
-        }
-
-        public MinBackhaulThresholdNetwork() {
-        }
-
-        public MinBackhaulThresholdNetwork(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeString(NetworkType);
-            out.writeString(DLBandwidth);
-            out.writeString(ULBandwidth);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                NetworkType = in.readString();
-                DLBandwidth = in.readString();
-                ULBandwidth = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<MinBackhaulThresholdNetwork> CREATOR = new Parcelable.Creator<MinBackhaulThresholdNetwork>() {
-            public MinBackhaulThresholdNetwork createFromParcel(Parcel in) {
-                return new MinBackhaulThresholdNetwork(in);
-            }
-
-            public MinBackhaulThresholdNetwork[] newArray(int size) {
-                return new MinBackhaulThresholdNetwork[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Policy/PolicyUpdate
-     **/
-    public static class PolicyUpdate implements Parcelable {
-        public String UpdateInterval;
-        public String UpdateMethod;
-        public String Restriction;
-        public String URI;
-        public UsernamePassword usernamePassword = new UsernamePassword();
-        public String Other;
-        public TrustRoot trustRoot = new TrustRoot();
-
-        public PolicyUpdate() {
-        }
-
-        public PolicyUpdate(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(UpdateInterval);
-            out.writeString(UpdateMethod);
-            out.writeString(Restriction);
-            out.writeString(URI);
-            out.writeParcelable(usernamePassword, flags);
-            out.writeString(Other);
-            out.writeParcelable(trustRoot, flags);
-
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                UpdateInterval = in.readString();
-                UpdateMethod = in.readString();
-                Restriction = in.readString();
-                URI = in.readString();
-                usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
-                Other = in.readString();
-                trustRoot = in.readParcelable(TrustRoot.class.getClassLoader());
-            }
-        }
-
-        public static final Parcelable.Creator<PolicyUpdate> CREATOR = new Parcelable.Creator<PolicyUpdate>() {
-            public PolicyUpdate createFromParcel(Parcel in) {
-                return new PolicyUpdate(in);
-            }
-
-            public PolicyUpdate[] newArray(int size) {
-                return new PolicyUpdate[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Policy/SPExclusionList
-     **/
-    public static class SPExclusionList implements Parcelable {
-        public String nodeName;
-        public String SSID;
-
-        public SPExclusionList(String nn, String s) {
-            nodeName = nn;
-            SSID = s;
-        }
-
-        public SPExclusionList() {
-        }
-
-        public SPExclusionList(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeString(SSID);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                SSID = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<SPExclusionList> CREATOR = new Parcelable.Creator<SPExclusionList>() {
-            public SPExclusionList createFromParcel(Parcel in) {
-                return new SPExclusionList(in);
-            }
-
-            public SPExclusionList[] newArray(int size) {
-                return new SPExclusionList[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Policy/RequiredProtoPortTuple
-     **/
-    public static class RequiredProtoPortTuple implements Parcelable {
-        public String nodeName;
-        public String IPProtocol;
-        public String PortNumber;
-
-        public RequiredProtoPortTuple() {
-        }
-
-        public RequiredProtoPortTuple(String nn, String protocol, String port) {
-            nodeName = nn;
-            IPProtocol = protocol;
-            PortNumber = port;
-        }
-
-        public RequiredProtoPortTuple(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeString(IPProtocol);
-            out.writeString(PortNumber);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                IPProtocol = in.readString();
-                PortNumber = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<RequiredProtoPortTuple> CREATOR = new Parcelable.Creator<RequiredProtoPortTuple>() {
-            public RequiredProtoPortTuple createFromParcel(Parcel in) {
-                return new RequiredProtoPortTuple(in);
-            }
-
-            public RequiredProtoPortTuple[] newArray(int size) {
-                return new RequiredProtoPortTuple[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/AAAServerTrustRoot
-     **/
-    public static class AAAServerTrustRoot implements Parcelable {
-        public String nodeName;
-        public String CertURL;
-        public String CertSHA256Fingerprint;
-
-        public AAAServerTrustRoot(String nn, String url, String fp) {
-            nodeName = nn;
-            CertURL = url;
-            CertSHA256Fingerprint = fp;
-        }
-
-        public AAAServerTrustRoot() {
-        }
-
-        public AAAServerTrustRoot(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeString(CertURL);
-            out.writeString(CertSHA256Fingerprint);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                CertURL = in.readString();
-                CertSHA256Fingerprint = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<AAAServerTrustRoot> CREATOR = new Parcelable.Creator<AAAServerTrustRoot>() {
-            public AAAServerTrustRoot createFromParcel(Parcel in) {
-                return new AAAServerTrustRoot(in);
-            }
-
-            public AAAServerTrustRoot[] newArray(int size) {
-                return new AAAServerTrustRoot[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/SubscriptionUpdate
-     **/
-    public static class SubscriptionUpdate implements Parcelable {
-        public String UpdateInterval;
-        public String UpdateMethod;
-        public String Restriction;
-        public String URI;
-        public UsernamePassword usernamePassword = new UsernamePassword();
-        public String Other;
-        public TrustRoot trustRoot = new TrustRoot();
-
-        public SubscriptionUpdate() {
-        }
-
-        public SubscriptionUpdate(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(UpdateInterval);
-            out.writeString(UpdateMethod);
-            out.writeString(Restriction);
-            out.writeString(URI);
-            out.writeParcelable(usernamePassword, flags);
-            out.writeString(Other);
-            out.writeParcelable(trustRoot, flags);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                UpdateInterval = in.readString();
-                UpdateMethod = in.readString();
-                Restriction = in.readString();
-                URI = in.readString();
-                usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
-                Other = in.readString();
-                trustRoot = in.readParcelable(TrustRoot.class.getClassLoader());
-            }
-        }
-
-        public static final Parcelable.Creator<SubscriptionUpdate> CREATOR = new Parcelable.Creator<SubscriptionUpdate>() {
-            public SubscriptionUpdate createFromParcel(Parcel in) {
-                return new SubscriptionUpdate(in);
-            }
-
-            public SubscriptionUpdate[] newArray(int size) {
-                return new SubscriptionUpdate[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Policy/PolicyUpdate/TrustRoot
-     * PerProviderSubscription/<X+>/SubscriptionUpdate/TrustRoot
-     * PerProviderSubscription/<X+>/AAAServerTrustRoot/<X+>
-     **/
-    public static class TrustRoot implements Parcelable {
-        public String CertURL;
-        public String CertSHA256Fingerprint;
-
-        public TrustRoot() {
-        }
-
-        public TrustRoot(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(CertURL);
-            out.writeString(CertSHA256Fingerprint);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                CertURL = in.readString();
-                CertSHA256Fingerprint = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<TrustRoot> CREATOR = new Parcelable.Creator<TrustRoot>() {
-            public TrustRoot createFromParcel(Parcel in) {
-                return new TrustRoot(in);
-            }
-
-            public TrustRoot[] newArray(int size) {
-                return new TrustRoot[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Policy/PolicyUpdate/UsernamePassword
-     * PerProviderSubscription/<X+>/SubscriptionUpdate/UsernamePassword
-     * PerProviderSubscription/<X+>/Credential/UsernamePassword
-     **/
-    public static class UsernamePassword implements Parcelable {
-        public String Username;
-        public String Password;
-        //following are Credential node used only
-        public boolean MachineManaged;
-        public String SoftTokenApp;
-        public String AbleToShare;
-        public EAPMethod eAPMethod = new EAPMethod();
-
-        public UsernamePassword() {
-        }
-
-        public UsernamePassword(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(Username);
-            out.writeString(Password);
-            out.writeInt(MachineManaged ? 1 : 0);
-            out.writeString(SoftTokenApp);
-            out.writeString(AbleToShare);
-            out.writeParcelable(eAPMethod, flags);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                Username = in.readString();
-                Password = in.readString();
-                MachineManaged = (in.readInt() == 1) ? true : false;
-                SoftTokenApp = in.readString();
-                AbleToShare = in.readString();
-                eAPMethod = in.readParcelable(EAPMethod.class.getClassLoader());
-            }
-        }
-
-        public static final Parcelable.Creator<UsernamePassword> CREATOR = new Parcelable.Creator<UsernamePassword>() {
-            public UsernamePassword createFromParcel(Parcel in) {
-                return new UsernamePassword(in);
-            }
-
-            public UsernamePassword[] newArray(int size) {
-                return new UsernamePassword[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Credential/UsernamePassword/EAPMethod
-     **/
-    public static class EAPMethod implements Parcelable {
-        public String EAPType;
-        public String VendorId;
-        public String VendorType;
-        public String InnerEAPType;
-        public String InnerVendorId;
-        public String InnerVendorType;
-        public String InnerMethod;
-
-        public EAPMethod() {
-        }
-
-        public EAPMethod(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(EAPType);
-            out.writeString(VendorId);
-            out.writeString(VendorType);
-            out.writeString(InnerEAPType);
-            out.writeString(InnerVendorId);
-            out.writeString(InnerVendorType);
-            out.writeString(InnerMethod);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                EAPType = in.readString();
-                VendorId = in.readString();
-                VendorType = in.readString();
-                InnerEAPType = in.readString();
-                InnerVendorId = in.readString();
-                InnerVendorType = in.readString();
-                InnerMethod = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<EAPMethod> CREATOR = new Parcelable.Creator<EAPMethod>() {
-            public EAPMethod createFromParcel(Parcel in) {
-                return new EAPMethod(in);
-            }
-
-            public EAPMethod[] newArray(int size) {
-                return new EAPMethod[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/HomeSP
-     **/
-    public static class HomeSP implements Parcelable {
-        public HashMap<String, NetworkID> networkID = new HashMap<String, NetworkID>();
-        public String FriendlyName;
-        public String IconURL;
-        public String FQDN;
-        public HashMap<String, HomeOIList> homeOIList = new HashMap<String, HomeOIList>();
-        public HashMap<String, OtherHomePartners> otherHomePartners = new HashMap<String, OtherHomePartners>();
-        public String RoamingConsortiumOI;
-
-        public NetworkID createNetworkID(String name, String ssid, String hessid) {
-            NetworkID obj = new NetworkID(name, ssid, hessid);
-            networkID.put(name, obj);
-            return obj;
-        }
-
-        public HomeOIList createHomeOIList(String name, String homeoi, boolean required) {
-            HomeOIList obj = new HomeOIList(name, homeoi, required);
-            homeOIList.put(name, obj);
-            return obj;
-        }
-
-        public OtherHomePartners createOtherHomePartners(String name, String fqdn) {
-            OtherHomePartners obj = new OtherHomePartners(name, fqdn);
-            otherHomePartners.put(name, obj);
-            return obj;
-        }
-
-        public HomeSP() {
-        }
-
-        public HomeSP(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeMap(networkID);
-            out.writeString(FriendlyName);
-            out.writeString(IconURL);
-            out.writeString(FQDN);
-            out.writeMap(homeOIList);
-            out.writeMap(otherHomePartners);
-            out.writeString(RoamingConsortiumOI);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                in.readMap(networkID, NetworkID.class.getClassLoader());
-                FriendlyName = in.readString();
-                IconURL = in.readString();
-                FQDN = in.readString();
-                in.readMap(homeOIList, HomeOIList.class.getClassLoader());
-                in.readMap(otherHomePartners, OtherHomePartners.class.getClassLoader());
-                RoamingConsortiumOI = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<HomeSP> CREATOR = new Parcelable.Creator<HomeSP>() {
-            public HomeSP createFromParcel(Parcel in) {
-                return new HomeSP(in);
-            }
-
-            public HomeSP[] newArray(int size) {
-                return new HomeSP[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/HomeSP/NetworkID
-     **/
-    public static class NetworkID implements Parcelable {
-        public String nodeName;
-        public String SSID;
-        public String HESSID;
-
-        public NetworkID(String nn, String s, String h) {
-            nodeName = nn;
-            SSID = s;
-            HESSID = h;
-        }
-
-        public NetworkID() {
-        }
-
-        public NetworkID(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeString(SSID);
-            out.writeString(HESSID);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                SSID = in.readString();
-                HESSID = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<NetworkID> CREATOR = new Parcelable.Creator<NetworkID>() {
-            public NetworkID createFromParcel(Parcel in) {
-                return new NetworkID(in);
-            }
-
-            public NetworkID[] newArray(int size) {
-                return new NetworkID[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/HomeSP/HomeOIList
-     **/
-    public static class HomeOIList implements Parcelable {
-        public String nodeName;
-        public String HomeOI;
-        public boolean HomeOIRequired;
-
-        public HomeOIList(String nn, String h, boolean r) {
-            nodeName = nn;
-            HomeOI = h;
-            HomeOIRequired = r;
-        }
-
-        public HomeOIList() {
-        }
-
-        public HomeOIList(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeString(HomeOI);
-            out.writeInt(HomeOIRequired ? 1 : 0);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                HomeOI = in.readString();
-                HomeOIRequired = (in.readInt() == 1) ? true : false;
-            }
-        }
-
-        public static final Parcelable.Creator<HomeOIList> CREATOR = new Parcelable.Creator<HomeOIList>() {
-            public HomeOIList createFromParcel(Parcel in) {
-                return new HomeOIList(in);
-            }
-
-            public HomeOIList[] newArray(int size) {
-                return new HomeOIList[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/HomeSP/OtherHomePartners
-     **/
-    public static class OtherHomePartners implements Parcelable {
-        public String nodeName;
-        public String FQDN;
-
-        public OtherHomePartners(String nn, String f) {
-            nodeName = nn;
-            FQDN = f;
-        }
-
-        public OtherHomePartners() {
-        }
-
-        public OtherHomePartners(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(nodeName);
-            out.writeString(FQDN);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                nodeName = in.readString();
-                FQDN = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<OtherHomePartners> CREATOR = new Parcelable.Creator<OtherHomePartners>() {
-            public OtherHomePartners createFromParcel(Parcel in) {
-                return new OtherHomePartners(in);
-            }
-
-            public OtherHomePartners[] newArray(int size) {
-                return new OtherHomePartners[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/SubscriptionParameters
-     **/
-    public static class SubscriptionParameters implements Parcelable {
-        public String CreationDate;
-        public String ExpirationDate;
-        public String TypeOfSubscription;
-        public UsageLimits usageLimits = new UsageLimits();
-
-        public SubscriptionParameters() {
-        }
-
-        public SubscriptionParameters(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(CreationDate);
-            out.writeString(ExpirationDate);
-            out.writeString(TypeOfSubscription);
-            out.writeParcelable(usageLimits, flags);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                CreationDate = in.readString();
-                ExpirationDate = in.readString();
-                TypeOfSubscription = in.readString();
-                usageLimits = in.readParcelable(UsageLimits.class.getClassLoader());
-            }
-        }
-
-        public static final Parcelable.Creator<SubscriptionParameters> CREATOR = new Parcelable.Creator<SubscriptionParameters>() {
-            public SubscriptionParameters createFromParcel(Parcel in) {
-                return new SubscriptionParameters(in);
-            }
-
-            public SubscriptionParameters[] newArray(int size) {
-                return new SubscriptionParameters[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/SubscriptionParameters/UsageLimits
-     **/
-    public static class UsageLimits implements Parcelable {
-        public String DataLimit;
-        public String StartDate;
-        public String TimeLimit;
-        public String UsageTimePeriod;
-
-        public UsageLimits() {
-        }
-
-        public UsageLimits(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(DataLimit);
-            out.writeString(StartDate);
-            out.writeString(TimeLimit);
-            out.writeString(UsageTimePeriod);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                DataLimit = in.readString();
-                StartDate = in.readString();
-                TimeLimit = in.readString();
-                UsageTimePeriod = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<UsageLimits> CREATOR = new Parcelable.Creator<UsageLimits>() {
-            public UsageLimits createFromParcel(Parcel in) {
-                return new UsageLimits(in);
-            }
-
-            public UsageLimits[] newArray(int size) {
-                return new UsageLimits[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Credential
-     **/
-    public static class Credential implements Parcelable {
-        public String CreationDate;
-        public String ExpirationDate;
-        public UsernamePassword usernamePassword = new UsernamePassword();
-        public DigitalCertificate digitalCertificate = new DigitalCertificate();
-        public String Realm;
-        public boolean CheckAAAServerCertStatus;
-        public SIM sim = new SIM();
-
-        public Credential() {
-        }
-
-        public Credential(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(CreationDate);
-            out.writeString(ExpirationDate);
-            out.writeParcelable(usernamePassword, flags);
-            out.writeParcelable(digitalCertificate, flags);
-            out.writeString(Realm);
-            out.writeInt(CheckAAAServerCertStatus ? 1 : 0);
-            out.writeParcelable(sim, flags);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                CreationDate = in.readString();
-                ExpirationDate = in.readString();
-                usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
-                digitalCertificate = in.readParcelable(DigitalCertificate.class.getClassLoader());
-                Realm = in.readString();
-                CheckAAAServerCertStatus = (in.readInt() == 1) ? true : false;
-                sim = in.readParcelable(SIM.class.getClassLoader());
-            }
-        }
-
-        public static final Parcelable.Creator<Credential> CREATOR = new Parcelable.Creator<Credential>() {
-            public Credential createFromParcel(Parcel in) {
-                return new Credential(in);
-            }
-
-            public Credential[] newArray(int size) {
-                return new Credential[size];
-            }
-        };
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Credential/DigitalCertificate
-     **/
-    public static class DigitalCertificate implements Parcelable {
-        public String CertificateType;
-        public String CertSHA256Fingerprint;
-
-        public DigitalCertificate() {
-        }
-
-        public DigitalCertificate(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(CertificateType);
-            out.writeString(CertSHA256Fingerprint);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                CertificateType = in.readString();
-                CertSHA256Fingerprint = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<DigitalCertificate> CREATOR = new Parcelable.Creator<DigitalCertificate>() {
-            public DigitalCertificate createFromParcel(Parcel in) {
-                return new DigitalCertificate(in);
-            }
-
-            public DigitalCertificate[] newArray(int size) {
-                return new DigitalCertificate[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Credential/SIM
-     **/
-    public static class SIM implements Parcelable {
-        public String IMSI;
-        public String EAPType;
-
-        public SIM() {
-        }
-
-        public SIM(Parcel in) {
-            readFromParcel(in);
-        }
-
-        public int describeContents() {
-            return 0;
-        }
-
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(IMSI);
-            out.writeString(EAPType);
-        }
-
-        public void readFromParcel(Parcel in) {
-            if (in == null) {
-                //log here
-            } else {
-                IMSI = in.readString();
-                EAPType = in.readString();
-            }
-        }
-
-        public static final Parcelable.Creator<SIM> CREATOR = new Parcelable.Creator<SIM>() {
-            public SIM createFromParcel(Parcel in) {
-                return new SIM(in);
-            }
-
-            public SIM[] newArray(int size) {
-                return new SIM[size];
-            }
-        };
-
-    }
-
-    /**
-     * PerProviderSubscription/<X+>/Extension
-     **/
-    public static class Extension {
-        public String empty;
-    }
-
-    public WifiPasspointDmTree() {
-    }
-
-    public WifiPasspointDmTree(Parcel in) {
-        readFromParcel(in);
-    }
-
-    public int describeContents() {
-        return 0;
-    }
-
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeMap(spFqdn);
-    }
-
-    public void readFromParcel(Parcel in) {
-        if (in == null) {
-            //log here
-        } else {
-            in.readMap(spFqdn, SpFqdn.class.getClassLoader());
-        }
-    }
-
-    public static final Parcelable.Creator<WifiPasspointDmTree> CREATOR = new Parcelable.Creator<WifiPasspointDmTree>() {
-        public WifiPasspointDmTree createFromParcel(Parcel in) {
-            return new WifiPasspointDmTree(in);
-        }
-
-        public WifiPasspointDmTree[] newArray(int size) {
-            return new WifiPasspointDmTree[size];
-        }
-    };
-
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl
deleted file mode 100644
index 27f23bc..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointInfo;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
deleted file mode 100644
index 33db3f5..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** @hide */
-public class WifiPasspointInfo implements Parcelable {
-
-    /** TODO doc */
-    public static final int ANQP_CAPABILITY = 1 << 0;
-
-    /** TODO doc */
-    public static final int VENUE_NAME = 1 << 1;
-
-    /** TODO doc */
-    public static final int NETWORK_AUTH_TYPE = 1 << 2;
-
-    /** TODO doc */
-    public static final int ROAMING_CONSORTIUM = 1 << 3;
-
-    /** TODO doc */
-    public static final int IP_ADDR_TYPE_AVAILABILITY = 1 << 4;
-
-    /** TODO doc */
-    public static final int NAI_REALM = 1 << 5;
-
-    /** TODO doc */
-    public static final int CELLULAR_NETWORK = 1 << 6;
-
-    /** TODO doc */
-    public static final int DOMAIN_NAME = 1 << 7;
-
-    /** TODO doc */
-    public static final int HOTSPOT_CAPABILITY = 1 << 8;
-
-    /** TODO doc */
-    public static final int OPERATOR_FRIENDLY_NAME = 1 << 9;
-
-    /** TODO doc */
-    public static final int WAN_METRICS = 1 << 10;
-
-    /** TODO doc */
-    public static final int CONNECTION_CAPABILITY = 1 << 11;
-
-    /** TODO doc */
-    public static final int OSU_PROVIDER = 1 << 12;
-
-    /** TODO doc */
-    public static final int PRESET_CRED_MATCH =
-            ANQP_CAPABILITY |
-                    HOTSPOT_CAPABILITY |
-                    NAI_REALM |
-                    CELLULAR_NETWORK |
-                    DOMAIN_NAME;
-
-    /** TODO doc */
-    public static final int PRESET_ALL =
-            ANQP_CAPABILITY |
-                    VENUE_NAME |
-                    NETWORK_AUTH_TYPE |
-                    ROAMING_CONSORTIUM |
-                    IP_ADDR_TYPE_AVAILABILITY |
-                    NAI_REALM |
-                    CELLULAR_NETWORK |
-                    DOMAIN_NAME |
-                    HOTSPOT_CAPABILITY |
-                    OPERATOR_FRIENDLY_NAME |
-                    WAN_METRICS |
-                    CONNECTION_CAPABILITY |
-                    OSU_PROVIDER;
-
-
-    public static class WanMetrics {
-        public static final int STATUS_RESERVED = 0;
-        public static final int STATUS_UP = 1;
-        public static final int STATUS_DOWN = 2;
-        public static final int STATUS_TEST = 3;
-
-        public int wanInfo;
-        public long downlinkSpeed;
-        public long uplinkSpeed;
-        public int downlinkLoad;
-        public int uplinkLoad;
-        public int lmd;
-
-        public int getLinkStatus() {
-            return wanInfo & 0x3;
-        }
-
-        public boolean getSymmetricLink() {
-            return (wanInfo & (1 << 2)) != 0;
-        }
-
-        public boolean getAtCapacity() {
-            return (wanInfo & (1 << 3)) != 0;
-        }
-
-        @Override
-        public String toString() {
-            return wanInfo + "," + downlinkSpeed + "," + uplinkSpeed + "," +
-                    downlinkLoad + "," + uplinkLoad + "," + lmd;
-        }
-    }
-
-    public static class IpProtoPort {
-        public static final int STATUS_CLOSED = 0;
-        public static final int STATUS_OPEN = 1;
-        public static final int STATUS_UNKNOWN = 2;
-
-        public int proto;
-        public int port;
-        public int status;
-
-        @Override
-        public String toString() {
-            return proto + "," + port + "," + status;
-        }
-    }
-
-    public static class NetworkAuthType {
-        public static final int TYPE_TERMS_AND_CONDITION = 0;
-        public static final int TYPE_ONLINE_ENROLLMENT = 1;
-        public static final int TYPE_HTTP_REDIRECTION = 2;
-        public static final int TYPE_DNS_REDIRECTION = 3;
-
-        public int type;
-        public String redirectUrl;
-
-        @Override
-        public String toString() {
-            return type + "," + redirectUrl;
-        }
-    }
-
-    public static class IpAddressType {
-        public static final int IPV6_NOT_AVAILABLE = 0;
-        public static final int IPV6_AVAILABLE = 1;
-        public static final int IPV6_UNKNOWN = 2;
-
-        public static final int IPV4_NOT_AVAILABLE = 0;
-        public static final int IPV4_PUBLIC = 1;
-        public static final int IPV4_PORT_RESTRICTED = 2;
-        public static final int IPV4_SINGLE_NAT = 3;
-        public static final int IPV4_DOUBLE_NAT = 4;
-        public static final int IPV4_PORT_RESTRICTED_SINGLE_NAT = 5;
-        public static final int IPV4_PORT_RESTRICTED_DOUBLE_NAT = 6;
-        public static final int IPV4_PORT_UNKNOWN = 7;
-
-        private static final int NULL_VALUE = -1;
-
-        public int availability;
-
-        public int getIpv6Availability() {
-            return availability & 0x3;
-        }
-
-        public int getIpv4Availability() {
-            return (availability & 0xFF) >> 2;
-        }
-
-        @Override
-        public String toString() {
-            return getIpv6Availability() + "," + getIpv4Availability();
-        }
-    }
-
-    public static class NaiRealm {
-        public static final int ENCODING_RFC4282 = 0;
-        public static final int ENCODING_UTF8 = 1;
-
-        public int encoding;
-        public String realm;
-
-        @Override
-        public String toString() {
-            return encoding + "," + realm;
-        }
-    }
-
-    public static class CellularNetwork {
-        public String mcc;
-        public String mnc;
-
-        @Override
-        public String toString() {
-            return mcc + "," + mnc;
-        }
-    }
-
-    /** BSSID */
-    public String bssid;
-
-    /** venue name */
-    public String venueName;
-
-    /** list of network authentication types */
-    public List<NetworkAuthType> networkAuthTypeList;
-
-    /** list of roaming consortium OIs */
-    public List<String> roamingConsortiumList;
-
-    /** IP address availability */
-    public IpAddressType ipAddrTypeAvailability;
-
-    /** list of NAI realm */
-    public List<NaiRealm> naiRealmList;
-
-    /** list of 3GPP cellular network */
-    public List<CellularNetwork> cellularNetworkList;
-
-    /** list of fully qualified domain name (FQDN) */
-    public List<String> domainNameList;
-
-    /** HS 2.0 operator friendly name */
-    public String operatorFriendlyName;
-
-    /** HS 2.0 wan metrics */
-    public WanMetrics wanMetrics;
-
-    /** list of HS 2.0 IP proto port */
-    public List<IpProtoPort> connectionCapabilityList;
-
-    /** list of HS 2.0 OSU providers */
-    public List<WifiPasspointOsuProvider> osuProviderList;
-
-    /**
-     * Convert mask to ANQP subtypes, for supplicant command use.
-     *
-     * @param mask The ANQP subtypes mask.
-     * @return String of ANQP subtypes, good for supplicant command use
-     * @hide
-     */
-    public static String toAnqpSubtypes(int mask) {
-        StringBuilder sb = new StringBuilder();
-        if ((mask & ANQP_CAPABILITY) != 0)
-            sb.append("257,");
-        if ((mask & VENUE_NAME) != 0)
-            sb.append("258,");
-        if ((mask & NETWORK_AUTH_TYPE) != 0)
-            sb.append("260,");
-        if ((mask & ROAMING_CONSORTIUM) != 0)
-            sb.append("261,");
-        if ((mask & IP_ADDR_TYPE_AVAILABILITY) != 0)
-            sb.append("262,");
-        if ((mask & NAI_REALM) != 0)
-            sb.append("263,");
-        if ((mask & CELLULAR_NETWORK) != 0)
-            sb.append("264,");
-        if ((mask & DOMAIN_NAME) != 0)
-            sb.append("268,");
-        if ((mask & HOTSPOT_CAPABILITY) != 0)
-            sb.append("hs20:2,");
-        if ((mask & OPERATOR_FRIENDLY_NAME) != 0)
-            sb.append("hs20:3,");
-        if ((mask & WAN_METRICS) != 0)
-            sb.append("hs20:4,");
-        if ((mask & CONNECTION_CAPABILITY) != 0)
-            sb.append("hs20:5,");
-        if ((mask & OSU_PROVIDER) != 0)
-            sb.append("hs20:8,");
-        if (sb.length() > 0)
-            sb.deleteCharAt(sb.length() - 1);
-        return sb.toString();
-    }
-
-    @Override
-    public String toString() {
-        StringBuffer sb = new StringBuffer();
-
-        sb.append("BSSID: ").append("(").append(bssid).append(")");
-
-        if (venueName != null)
-            sb.append(" venueName: ").append("(")
-              .append(venueName.replace("\n", "\\n")).append(")");
-
-        if (networkAuthTypeList != null) {
-            sb.append(" networkAuthType: ");
-            for (NetworkAuthType auth : networkAuthTypeList)
-                sb.append("(").append(auth.toString()).append(")");
-        }
-
-        if (roamingConsortiumList != null) {
-            sb.append(" roamingConsortium: ");
-            for (String oi : roamingConsortiumList)
-                sb.append("(").append(oi).append(")");
-        }
-
-        if (ipAddrTypeAvailability != null) {
-            sb.append(" ipAddrTypeAvaibility: ").append("(")
-              .append(ipAddrTypeAvailability.toString()).append(")");
-        }
-
-        if (naiRealmList != null) {
-            sb.append(" naiRealm: ");
-            for (NaiRealm realm : naiRealmList)
-                sb.append("(").append(realm.toString()).append(")");
-        }
-
-        if (cellularNetworkList != null) {
-            sb.append(" cellularNetwork: ");
-            for (CellularNetwork plmn : cellularNetworkList)
-                sb.append("(").append(plmn.toString()).append(")");
-        }
-
-        if (domainNameList != null) {
-            sb.append(" domainName: ");
-            for (String fqdn : domainNameList)
-                sb.append("(").append(fqdn).append(")");
-        }
-
-        if (operatorFriendlyName != null)
-            sb.append(" operatorFriendlyName: ").append("(")
-              .append(operatorFriendlyName).append(")");
-
-        if (wanMetrics != null)
-            sb.append(" wanMetrics: ").append("(")
-              .append(wanMetrics.toString()).append(")");
-
-        if (connectionCapabilityList != null) {
-            sb.append(" connectionCapability: ");
-            for (IpProtoPort ip : connectionCapabilityList)
-                sb.append("(").append(ip.toString()).append(")");
-        }
-
-        if (osuProviderList != null) {
-            sb.append(" osuProviderList: ");
-            for (WifiPasspointOsuProvider osu : osuProviderList)
-                sb.append("(").append(osu.toString()).append(")");
-        }
-
-        return sb.toString();
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(bssid);
-        out.writeString(venueName);
-
-        if (networkAuthTypeList == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(networkAuthTypeList.size());
-            for (NetworkAuthType auth : networkAuthTypeList) {
-                out.writeInt(auth.type);
-                out.writeString(auth.redirectUrl);
-            }
-        }
-
-        if (roamingConsortiumList == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(roamingConsortiumList.size());
-            for (String oi : roamingConsortiumList)
-                out.writeString(oi);
-        }
-
-        if (ipAddrTypeAvailability == null) {
-            out.writeInt(IpAddressType.NULL_VALUE);
-        } else {
-            out.writeInt(ipAddrTypeAvailability.availability);
-        }
-
-        if (naiRealmList == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(naiRealmList.size());
-            for (NaiRealm realm : naiRealmList) {
-                out.writeInt(realm.encoding);
-                out.writeString(realm.realm);
-            }
-        }
-
-        if (cellularNetworkList == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(cellularNetworkList.size());
-            for (CellularNetwork plmn : cellularNetworkList) {
-                out.writeString(plmn.mcc);
-                out.writeString(plmn.mnc);
-            }
-        }
-
-
-        if (domainNameList == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(domainNameList.size());
-            for (String fqdn : domainNameList)
-                out.writeString(fqdn);
-        }
-
-        out.writeString(operatorFriendlyName);
-
-        if (wanMetrics == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(1);
-            out.writeInt(wanMetrics.wanInfo);
-            out.writeLong(wanMetrics.downlinkSpeed);
-            out.writeLong(wanMetrics.uplinkSpeed);
-            out.writeInt(wanMetrics.downlinkLoad);
-            out.writeInt(wanMetrics.uplinkLoad);
-            out.writeInt(wanMetrics.lmd);
-        }
-
-        if (connectionCapabilityList == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(connectionCapabilityList.size());
-            for (IpProtoPort ip : connectionCapabilityList) {
-                out.writeInt(ip.proto);
-                out.writeInt(ip.port);
-                out.writeInt(ip.status);
-            }
-        }
-
-        if (osuProviderList == null) {
-            out.writeInt(0);
-        } else {
-            out.writeInt(osuProviderList.size());
-            for (WifiPasspointOsuProvider osu : osuProviderList)
-                osu.writeToParcel(out, flags);
-        }
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public static final Parcelable.Creator<WifiPasspointInfo> CREATOR =
-            new Parcelable.Creator<WifiPasspointInfo>() {
-                @Override
-                public WifiPasspointInfo createFromParcel(Parcel in) {
-                    WifiPasspointInfo p = new WifiPasspointInfo();
-                    int n;
-
-                    p.bssid = in.readString();
-                    p.venueName = in.readString();
-
-                    n = in.readInt();
-                    if (n > 0) {
-                        p.networkAuthTypeList = new ArrayList<NetworkAuthType>();
-                        for (int i = 0; i < n; i++) {
-                            NetworkAuthType auth = new NetworkAuthType();
-                            auth.type = in.readInt();
-                            auth.redirectUrl = in.readString();
-                            p.networkAuthTypeList.add(auth);
-                        }
-                    }
-
-                    n = in.readInt();
-                    if (n > 0) {
-                        p.roamingConsortiumList = new ArrayList<String>();
-                        for (int i = 0; i < n; i++)
-                            p.roamingConsortiumList.add(in.readString());
-                    }
-
-                    n = in.readInt();
-                    if (n != IpAddressType.NULL_VALUE) {
-                        p.ipAddrTypeAvailability = new IpAddressType();
-                        p.ipAddrTypeAvailability.availability = n;
-                    }
-
-                    n = in.readInt();
-                    if (n > 0) {
-                        p.naiRealmList = new ArrayList<NaiRealm>();
-                        for (int i = 0; i < n; i++) {
-                            NaiRealm realm = new NaiRealm();
-                            realm.encoding = in.readInt();
-                            realm.realm = in.readString();
-                            p.naiRealmList.add(realm);
-                        }
-                    }
-
-                    n = in.readInt();
-                    if (n > 0) {
-                        p.cellularNetworkList = new ArrayList<CellularNetwork>();
-                        for (int i = 0; i < n; i++) {
-                            CellularNetwork plmn = new CellularNetwork();
-                            plmn.mcc = in.readString();
-                            plmn.mnc = in.readString();
-                            p.cellularNetworkList.add(plmn);
-                        }
-                    }
-
-                    n = in.readInt();
-                    if (n > 0) {
-                        p.domainNameList = new ArrayList<String>();
-                        for (int i = 0; i < n; i++)
-                            p.domainNameList.add(in.readString());
-                    }
-
-                    p.operatorFriendlyName = in.readString();
-
-                    n = in.readInt();
-                    if (n > 0) {
-                        p.wanMetrics = new WanMetrics();
-                        p.wanMetrics.wanInfo = in.readInt();
-                        p.wanMetrics.downlinkSpeed = in.readLong();
-                        p.wanMetrics.uplinkSpeed = in.readLong();
-                        p.wanMetrics.downlinkLoad = in.readInt();
-                        p.wanMetrics.uplinkLoad = in.readInt();
-                        p.wanMetrics.lmd = in.readInt();
-                    }
-
-                    n = in.readInt();
-                    if (n > 0) {
-                        p.connectionCapabilityList = new ArrayList<IpProtoPort>();
-                        for (int i = 0; i < n; i++) {
-                            IpProtoPort ip = new IpProtoPort();
-                            ip.proto = in.readInt();
-                            ip.port = in.readInt();
-                            ip.status = in.readInt();
-                            p.connectionCapabilityList.add(ip);
-                        }
-                    }
-
-                    n = in.readInt();
-                    if (n > 0) {
-                        p.osuProviderList = new ArrayList<WifiPasspointOsuProvider>();
-                        for (int i = 0; i < n; i++) {
-                            WifiPasspointOsuProvider osu = WifiPasspointOsuProvider.CREATOR
-                                    .createFromParcel(in);
-                            p.osuProviderList.add(osu);
-                        }
-                    }
-
-                    return p;
-                }
-
-                @Override
-                public WifiPasspointInfo[] newArray(int size) {
-                    return new WifiPasspointInfo[size];
-                }
-            };
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
deleted file mode 100644
index 0245a3d..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.content.Context;
-import android.net.wifi.ScanResult;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.Protocol;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Provides APIs for managing Wifi Passpoint credentials.
- * @hide
- */
-public class WifiPasspointManager {
-
-    private static final String TAG = "PasspointManager";
-
-    private static final boolean DBG = true;
-
-    /* Passpoint states values */
-
-    /** Passpoint is in an unknown state. This should only occur in boot time */
-    public static final int PASSPOINT_STATE_UNKNOWN = 0;
-
-    /** Passpoint is disabled. This occurs when wifi is disabled */
-    public static final int PASSPOINT_STATE_DISABLED = 1;
-
-    /** Passpoint is enabled and in discovery state */
-    public static final int PASSPOINT_STATE_DISCOVERY = 2;
-
-    /** Passpoint is enabled and in access state */
-    public static final int PASSPOINT_STATE_ACCESS = 3;
-
-    /** Passpoint is enabled and in provisioning state */
-    public static final int PASSPOINT_STATE_PROVISION = 4;
-
-    /* Passpoint callback error codes */
-
-    /** Indicates that the operation failed due to an internal error */
-    public static final int REASON_ERROR = 0;
-
-    /** Indicates that the operation failed because wifi is disabled */
-    public static final int REASON_WIFI_DISABLED = 1;
-
-    /** Indicates that the operation failed because the framework is busy */
-    public static final int REASON_BUSY = 2;
-
-    /** Indicates that the operation failed because parameter is invalid */
-    public static final int REASON_INVALID_PARAMETER = 3;
-
-    /** Indicates that the operation failed because the server is not trusted */
-    public static final int REASON_NOT_TRUSTED = 4;
-
-    /**
-     * protocol supported for Passpoint
-     */
-    public static final String PROTOCOL_DM = "OMA-DM-ClientInitiated";
-
-    /**
-     * protocol supported for Passpoint
-     */
-    public static final String PROTOCOL_SOAP = "SPP-ClientInitiated";
-
-    /* Passpoint broadcasts */
-
-    /**
-     * Broadcast intent action indicating that the state of Passpoint
-     * connectivity has changed
-     */
-    public static final String PASSPOINT_STATE_CHANGED_ACTION =
-            "android.net.wifi.passpoint.STATE_CHANGE";
-
-    /**
-     * Broadcast intent action indicating that the saved Passpoint credential
-     * list has changed
-     */
-    public static final String PASSPOINT_CRED_CHANGED_ACTION =
-            "android.net.wifi.passpoint.CRED_CHANGE";
-
-    /**
-     * Broadcast intent action indicating that Passpoint online sign up is
-     * avaiable.
-     */
-    public static final String PASSPOINT_OSU_AVAILABLE_ACTION =
-            "android.net.wifi.passpoint.OSU_AVAILABLE";
-
-    /**
-     * Broadcast intent action indicating that user remediation is required
-     */
-    public static final String PASSPOINT_USER_REM_REQ_ACTION =
-            "android.net.wifi.passpoint.USER_REM_REQ";
-
-    /**
-     * Interface for callback invocation when framework channel is lost
-     */
-    public interface ChannelListener {
-        /**
-         * The channel to the framework has been disconnected. Application could
-         * try re-initializing using {@link #initialize}
-         */
-        public void onChannelDisconnected();
-    }
-
-    /**
-     * Interface for callback invocation on an application action
-     */
-    public interface ActionListener {
-        /** The operation succeeded */
-        public void onSuccess();
-
-        /**
-         * The operation failed
-         *
-         * @param reason The reason for failure could be one of
-         *            {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY}
-         */
-        public void onFailure(int reason);
-    }
-
-    /**
-     * Interface for callback invocation when doing OSU or user remediation
-     */
-    public interface OsuRemListener {
-        /** The operation succeeded */
-        public void onSuccess();
-
-        /**
-         * The operation failed
-         *
-         * @param reason The reason for failure could be one of
-         *            {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY}
-         */
-        public void onFailure(int reason);
-
-        /**
-         * Browser launch is requried for user interaction. When this callback
-         * is called, app should launch browser / webview to the given URI.
-         *
-         * @param uri URI for browser launch
-         */
-        public void onBrowserLaunch(String uri);
-
-        /**
-         * When this is called, app should dismiss the previously lanched browser.
-         */
-        public void onBrowserDismiss();
-    }
-
-    /**
-     * A channel that connects the application to the wifi passpoint framework.
-     * Most passpoint operations require a Channel as an argument.
-     * An instance of Channel is obtained by doing a call on {@link #initialize}
-     */
-    public static class Channel {
-        private final static int INVALID_LISTENER_KEY = 0;
-
-        private ChannelListener mChannelListener;
-
-        private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
-        private HashMap<Integer, Integer> mListenerMapCount = new HashMap<Integer, Integer>();
-        private Object mListenerMapLock = new Object();
-        private int mListenerKey = 0;
-
-        private List<ScanResult> mAnqpRequest = new LinkedList<ScanResult>();
-        private Object mAnqpRequestLock = new Object();
-
-        private AsyncChannel mAsyncChannel;
-        private PasspointHandler mHandler;
-        Context mContext;
-
-        Channel(Context context, Looper looper, ChannelListener l) {
-            mAsyncChannel = new AsyncChannel();
-            mHandler = new PasspointHandler(looper);
-            mChannelListener = l;
-            mContext = context;
-        }
-
-        private int putListener(Object listener) {
-            return putListener(listener, 1);
-        }
-
-        private int putListener(Object listener, int count) {
-            if (listener == null || count <= 0)
-                return INVALID_LISTENER_KEY;
-            int key;
-            synchronized (mListenerMapLock) {
-                do {
-                    key = mListenerKey++;
-                } while (key == INVALID_LISTENER_KEY);
-                mListenerMap.put(key, listener);
-                mListenerMapCount.put(key, count);
-            }
-            return key;
-        }
-
-        private Object peekListener(int key) {
-            Log.d(TAG, "peekListener() key=" + key);
-            if (key == INVALID_LISTENER_KEY)
-                return null;
-            synchronized (mListenerMapLock) {
-                return mListenerMap.get(key);
-            }
-        }
-
-
-        private Object getListener(int key, boolean forceRemove) {
-            Log.d(TAG, "getListener() key=" + key + " force=" + forceRemove);
-            if (key == INVALID_LISTENER_KEY)
-                return null;
-            synchronized (mListenerMapLock) {
-                if (!forceRemove) {
-                    int count = mListenerMapCount.get(key);
-                    Log.d(TAG, "count=" + count);
-                    mListenerMapCount.put(key, --count);
-                    if (count > 0)
-                        return null;
-                }
-                Log.d(TAG, "remove key");
-                mListenerMapCount.remove(key);
-                return mListenerMap.remove(key);
-            }
-        }
-
-        private void anqpRequestStart(ScanResult sr) {
-            Log.d(TAG, "anqpRequestStart sr.bssid=" + sr.BSSID);
-            synchronized (mAnqpRequestLock) {
-                mAnqpRequest.add(sr);
-            }
-        }
-
-        private void anqpRequestFinish(WifiPasspointInfo result) {
-            Log.d(TAG, "anqpRequestFinish pi.bssid=" + result.bssid);
-            synchronized (mAnqpRequestLock) {
-                for (ScanResult sr : mAnqpRequest)
-                    if (sr.BSSID.equals(result.bssid)) {
-                        Log.d(TAG, "find hit " + result.bssid);
-                        /* sr.passpoint = result; */
-                        mAnqpRequest.remove(sr);
-                        Log.d(TAG, "mAnqpRequest.len=" + mAnqpRequest.size());
-                        break;
-                    }
-            }
-        }
-
-        private void anqpRequestFinish(ScanResult sr) {
-            Log.d(TAG, "anqpRequestFinish sr.bssid=" + sr.BSSID);
-            synchronized (mAnqpRequestLock) {
-                for (ScanResult sr1 : mAnqpRequest)
-                    if (sr1.BSSID.equals(sr.BSSID)) {
-                        mAnqpRequest.remove(sr1);
-                        break;
-                    }
-            }
-        }
-
-        class PasspointHandler extends Handler {
-            PasspointHandler(Looper looper) {
-                super(looper);
-            }
-
-            @Override
-            public void handleMessage(Message message) {
-                Object listener = null;
-
-                switch (message.what) {
-                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
-                        if (mChannelListener != null) {
-                            mChannelListener.onChannelDisconnected();
-                            mChannelListener = null;
-                        }
-                        break;
-
-                    case REQUEST_ANQP_INFO_SUCCEEDED:
-                        WifiPasspointInfo result = (WifiPasspointInfo) message.obj;
-                        anqpRequestFinish(result);
-                        listener = getListener(message.arg2, false);
-                        if (listener != null) {
-                            ((ActionListener) listener).onSuccess();
-                        }
-                        break;
-
-                    case REQUEST_ANQP_INFO_FAILED:
-                        anqpRequestFinish((ScanResult) message.obj);
-                        listener = getListener(message.arg2, false);
-                        if (listener == null)
-                            getListener(message.arg2, true);
-                        if (listener != null) {
-                            ((ActionListener) listener).onFailure(message.arg1);
-                        }
-                        break;
-
-                    case START_OSU_SUCCEEDED:
-                        listener = getListener(message.arg2, true);
-                        if (listener != null) {
-                            ((OsuRemListener) listener).onSuccess();
-                        }
-                        break;
-
-                    case START_OSU_FAILED:
-                        listener = getListener(message.arg2, true);
-                        if (listener != null) {
-                            ((OsuRemListener) listener).onFailure(message.arg1);
-                        }
-                        break;
-
-                    case START_OSU_BROWSER:
-                        listener = peekListener(message.arg2);
-                        if (listener != null) {
-                            ParcelableString str = (ParcelableString) message.obj;
-                            if (str == null || str.string == null)
-                                ((OsuRemListener) listener).onBrowserDismiss();
-                            else
-                                ((OsuRemListener) listener).onBrowserLaunch(str.string);
-                        }
-                        break;
-
-                    default:
-                        Log.d(TAG, "Ignored " + message);
-                        break;
-                }
-            }
-        }
-
-    }
-
-    public static class ParcelableString implements Parcelable {
-        public String string;
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeString(string);
-        }
-
-        public static final Parcelable.Creator<ParcelableString> CREATOR =
-                new Parcelable.Creator<ParcelableString>() {
-                    @Override
-                    public ParcelableString createFromParcel(Parcel in) {
-                        ParcelableString ret = new ParcelableString();
-                        ret.string = in.readString();
-                        return ret;
-                    }
-                    @Override
-                    public ParcelableString[] newArray(int size) {
-                        return new ParcelableString[size];
-                    }
-        };
-    }
-
-    private static final int BASE = Protocol.BASE_WIFI_PASSPOINT_MANAGER;
-
-    public static final int REQUEST_ANQP_INFO                   = BASE + 1;
-    public static final int REQUEST_ANQP_INFO_FAILED            = BASE + 2;
-    public static final int REQUEST_ANQP_INFO_SUCCEEDED         = BASE + 3;
-    public static final int REQUEST_OSU_ICON                    = BASE + 4;
-    public static final int REQUEST_OSU_ICON_FAILED             = BASE + 5;
-    public static final int REQUEST_OSU_ICON_SUCCEEDED          = BASE + 6;
-    public static final int START_OSU                           = BASE + 7;
-    public static final int START_OSU_BROWSER                   = BASE + 8;
-    public static final int START_OSU_FAILED                    = BASE + 9;
-    public static final int START_OSU_SUCCEEDED                 = BASE + 10;
-
-    private Context mContext;
-    IWifiPasspointManager mService;
-
-    /**
-     * TODO: doc
-     * @param context
-     * @param service
-     */
-    public WifiPasspointManager(Context context, IWifiPasspointManager service) {
-        mContext = context;
-        mService = service;
-    }
-
-    /**
-     * Registers the application with the framework. This function must be the
-     * first to be called before any async passpoint operations are performed.
-     *
-     * @param srcContext is the context of the source
-     * @param srcLooper is the Looper on which the callbacks are receivied
-     * @param listener for callback at loss of framework communication. Can be
-     *            null.
-     * @return Channel instance that is necessary for performing any further
-     *         passpoint operations
-     *
-     */
-    public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
-        Messenger messenger = getMessenger();
-        if (messenger == null)
-            return null;
-
-        Channel c = new Channel(srcContext, srcLooper, listener);
-        if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
-                == AsyncChannel.STATUS_SUCCESSFUL) {
-            return c;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * STOPSHIP: temp solution, should use supplicant manager instead, check
-     * with b/13931972
-     */
-    public Messenger getMessenger() {
-        try {
-            return mService.getMessenger();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    public int getPasspointState() {
-        try {
-            return mService.getPasspointState();
-        } catch (RemoteException e) {
-            return PASSPOINT_STATE_UNKNOWN;
-        }
-    }
-
-    public void requestAnqpInfo(Channel c, List<ScanResult> requested, int mask,
-            ActionListener listener) {
-        Log.d(TAG, "requestAnqpInfo start");
-        Log.d(TAG, "requested.size=" + requested.size());
-        checkChannel(c);
-        List<ScanResult> list = new ArrayList<ScanResult>();
-        for (ScanResult sr : requested)
-            if (sr.capabilities.contains("[HS20]")) {
-                list.add(sr);
-                c.anqpRequestStart(sr);
-                Log.d(TAG, "adding " + sr.BSSID);
-            }
-        int count = list.size();
-        Log.d(TAG, "after filter, count=" + count);
-        if (count == 0) {
-            if (DBG)
-                Log.d(TAG, "ANQP info request contains no HS20 APs, skipped");
-            listener.onSuccess();
-            return;
-        }
-        int key = c.putListener(listener, count);
-        for (ScanResult sr : list)
-            c.mAsyncChannel.sendMessage(REQUEST_ANQP_INFO, mask, key, sr);
-        Log.d(TAG, "requestAnqpInfo end");
-    }
-
-    public void requestOsuIcons(Channel c, List<WifiPasspointOsuProvider> requested,
-            int resolution, ActionListener listener) {
-    }
-
-    public List<WifiPasspointPolicy> requestCredentialMatch(List<ScanResult> requested) {
-        try {
-            return mService.requestCredentialMatch(requested);
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Get a list of saved Passpoint credentials. Only those credentials owned
-     * by the caller will be returned.
-     *
-     * @return The list of credentials
-     */
-    public List<WifiPasspointCredential> getCredentials() {
-        try {
-            return mService.getCredentials();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Add a new Passpoint credential.
-     *
-     * @param cred The credential to be added
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public boolean addCredential(WifiPasspointCredential cred) {
-        try {
-            return mService.addCredential(cred);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Update an existing Passpoint credential. Only system or the owner of this
-     * credential has the permission to do this.
-     *
-     * @param cred The credential to be updated
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public boolean updateCredential(WifiPasspointCredential cred) {
-        try {
-            return mService.updateCredential(cred);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Remove an existing Passpoint credential. Only system or the owner of this
-     * credential has the permission to do this.
-     *
-     * @param cred The credential to be removed
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public boolean removeCredential(WifiPasspointCredential cred) {
-        try {
-            return mService.removeCredential(cred);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    public void startOsu(Channel c, WifiPasspointOsuProvider osu, OsuRemListener listener) {
-        Log.d(TAG, "startOsu start");
-        checkChannel(c);
-        int key = c.putListener(listener);
-        c.mAsyncChannel.sendMessage(START_OSU, 0, key, osu);
-        Log.d(TAG, "startOsu end");
-    }
-
-    public void startRemediation(Channel c, OsuRemListener listener) {
-    }
-
-    public void connect(WifiPasspointPolicy policy) {
-    }
-
-    private static void checkChannel(Channel c) {
-        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
-    }
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl
deleted file mode 100644
index 088136f..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointOsuProvider;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
deleted file mode 100644
index b54b70c..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public class WifiPasspointOsuProvider implements Parcelable {
-
-    /** TODO: doc
-     * @hide
-     */
-    public static final int OSU_METHOD_UNKNOWN = -1;
-
-    /** TODO: doc
-     * @hide
-     */
-    public static final int OSU_METHOD_OMADM = 0;
-
-    /** TODO: doc
-     * @hide
-     */
-    public static final int OSU_METHOD_SOAP = 1;
-
-    /** TODO: doc */
-    public String ssid;
-
-    /** TODO: doc */
-    public String friendlyName;
-
-    /** TODO: doc
-     * @hide
-     */
-    public String serverUri;
-
-    /** TODO: doc
-     * @hide
-     */
-    public int osuMethod = OSU_METHOD_UNKNOWN;
-
-    /** TODO: doc */
-    public int iconWidth;
-
-    /** TODO: doc */
-    public int iconHeight;
-
-    /** TODO: doc */
-    public String iconType;
-
-    /** TODO: doc */
-    public String iconFileName;
-
-    /** TODO: doc */
-    public Object icon; // TODO: should change to image format
-
-    /** TODO: doc */
-    public String osuNai;
-
-    /** TODO: doc */
-    public String osuService;
-
-    /** default constructor @hide */
-    public WifiPasspointOsuProvider() {
-        // TODO
-    }
-
-    /** copy constructor @hide */
-    public WifiPasspointOsuProvider(WifiPasspointOsuProvider source) {
-        // TODO
-    }
-
-    @Override
-    public String toString() {
-        StringBuffer sb = new StringBuffer();
-        sb.append("SSID: ").append("<").append(ssid).append(">");
-        if (friendlyName != null)
-            sb.append(" friendlyName: ").append("<").append(friendlyName).append(">");
-        if (serverUri != null)
-            sb.append(" serverUri: ").append("<").append(serverUri).append(">");
-        sb.append(" osuMethod: ").append("<").append(osuMethod).append(">");
-        if (iconFileName != null) {
-            sb.append(" icon: <").append(iconWidth).append("x")
-                    .append(iconHeight).append(" ")
-                    .append(iconType).append(" ")
-                    .append(iconFileName).append(">");
-        }
-        if (osuNai != null)
-            sb.append(" osuNai: ").append("<").append(osuNai).append(">");
-        if (osuService != null)
-            sb.append(" osuService: ").append("<").append(osuService).append(">");
-        return sb.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(ssid);
-        out.writeString(friendlyName);
-        out.writeString(serverUri);
-        out.writeInt(osuMethod);
-        out.writeInt(iconWidth);
-        out.writeInt(iconHeight);
-        out.writeString(iconType);
-        out.writeString(iconFileName);
-        out.writeString(osuNai);
-        out.writeString(osuService);
-        // TODO: icon image?
-    }
-
-    public static final Parcelable.Creator<WifiPasspointOsuProvider> CREATOR =
-            new Parcelable.Creator<WifiPasspointOsuProvider>() {
-                @Override
-                public WifiPasspointOsuProvider createFromParcel(Parcel in) {
-                    WifiPasspointOsuProvider osu = new WifiPasspointOsuProvider();
-                    osu.ssid = in.readString();
-                    osu.friendlyName = in.readString();
-                    osu.serverUri = in.readString();
-                    osu.osuMethod = in.readInt();
-                    osu.iconWidth = in.readInt();
-                    osu.iconHeight = in.readInt();
-                    osu.iconType = in.readString();
-                    osu.iconFileName = in.readString();
-                    osu.osuNai = in.readString();
-                    osu.osuService = in.readString();
-                    return osu;
-                }
-
-                @Override
-                public WifiPasspointOsuProvider[] newArray(int size) {
-                    return new WifiPasspointOsuProvider[size];
-                }
-            };
-}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl
deleted file mode 100644
index 1d61da0..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-parcelable WifiPasspointPolicy;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
deleted file mode 100644
index c08877e..0000000
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.passpoint;
-
-import android.net.wifi.WifiConfiguration;
-import android.os.Parcelable;
-import android.os.Parcel;
-import android.security.Credentials;
-import android.util.Log;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-
-/** @hide */
-public class WifiPasspointPolicy implements Parcelable {
-
-    private final static String TAG = "PasspointPolicy";
-
-    /** @hide */
-    public static final int HOME_SP = 0;
-
-    /** @hide */
-    public static final int ROAMING_PARTNER = 1;
-
-    /** @hide */
-    public static final int UNRESTRICTED = 2;
-
-    private String mName;
-    private int mCredentialPriority;
-    private int mRoamingPriority;
-    private String mBssid;
-    private String mSsid;
-    private WifiPasspointCredential mCredential;
-    private int mRestriction;// Permitted values are "HomeSP", "RoamingPartner", or "Unrestricted"
-    private boolean mIsHomeSp;
-
-    private final String INT_PRIVATE_KEY = "private_key";
-    private final String INT_PHASE2 = "phase2";
-    private final String INT_PASSWORD = "password";
-    private final String INT_IDENTITY = "identity";
-    private final String INT_EAP = "eap";
-    private final String INT_CLIENT_CERT = "client_cert";
-    private final String INT_CA_CERT = "ca_cert";
-    private final String INT_ANONYMOUS_IDENTITY = "anonymous_identity";
-    private final String INT_SIM_SLOT = "sim_slot";
-    private final String INT_ENTERPRISEFIELD_NAME ="android.net.wifi.WifiConfiguration$EnterpriseField";
-    private final String ISO8601DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
-    private final String ENTERPRISE_PHASE2_MSCHAPV2 = "auth=MSCHAPV2";
-    private final String ENTERPRISE_PHASE2_MSCHAP = "auth=MSCHAP";
-
-    /** @hide */
-    public WifiPasspointPolicy(String name, String ssid,
-            String bssid, WifiPasspointCredential pc,
-            int restriction, boolean ishomesp) {
-        mName = name;
-        if (pc != null) {
-            mCredentialPriority = pc.getPriority();
-        }
-        //PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>/Priority
-        mRoamingPriority = 128; //default priority value of 128
-        mSsid = ssid;
-        mCredential = pc;
-        mBssid = bssid;
-        mRestriction = restriction;
-        mIsHomeSp = ishomesp;
-    }
-
-    public String getSsid() {
-        return mSsid;
-    }
-
-    /** @hide */
-    public void setBssid(String bssid) {
-        mBssid = bssid;
-    }
-
-    public String getBssid() {
-        return mBssid;
-    }
-
-    /** @hide */
-    public void setRestriction(int r) {
-        mRestriction = r;
-    }
-
-    /** @hide */
-    public int getRestriction() {
-        return mRestriction;
-    }
-
-    /** @hide */
-    public void setHomeSp(boolean b) {
-        mIsHomeSp = b;
-    }
-
-    /** @hide */
-    public boolean isHomeSp() {
-        return mIsHomeSp;
-    }
-
-    /** @hide */
-    public void setCredential(WifiPasspointCredential newCredential) {
-        mCredential = newCredential;
-    }
-
-    public WifiPasspointCredential getCredential() {
-        // TODO: return a copy
-        return mCredential;
-    }
-
-    /** @hide */
-    public void setCredentialPriority(int priority) {
-        mCredentialPriority = priority;
-    }
-
-    /** @hide */
-    public void setRoamingPriority(int priority) {
-        mRoamingPriority = priority;
-    }
-
-    public int getCredentialPriority() {
-        return mCredentialPriority;
-    }
-
-    public int getRoamingPriority() {
-        return mRoamingPriority;
-    }
-
-    public WifiConfiguration createWifiConfiguration() {
-        WifiConfiguration wfg = new WifiConfiguration();
-        if (mBssid != null) {
-            Log.d(TAG, "create bssid:" + mBssid);
-            wfg.BSSID = mBssid;
-        }
-
-        if (mSsid != null) {
-            Log.d(TAG, "create ssid:" + mSsid);
-            wfg.SSID = mSsid;
-        }
-        //TODO: 1. add pmf configuration
-        //      2. add ocsp configuration
-        //      3. add eap-sim configuration
-        /*Key management*/
-        wfg.status = WifiConfiguration.Status.ENABLED;
-        wfg.allowedKeyManagement.clear();
-        wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
-        wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
-
-        /*Group Ciphers*/
-        wfg.allowedGroupCiphers.clear();
-        wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
-        wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
-
-        /*Protocols*/
-        wfg.allowedProtocols.clear();
-        wfg.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
-        wfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
-
-        Class[] enterpriseFieldArray  = WifiConfiguration.class.getClasses();
-        Class<?> enterpriseFieldClass = null;
-
-
-        for(Class<?> myClass : enterpriseFieldArray) {
-            if(myClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) {
-                enterpriseFieldClass = myClass;
-                break;
-            }
-        }
-        Log.d(TAG, "class chosen " + enterpriseFieldClass.getName() );
-
-
-        Field anonymousId = null, caCert = null, clientCert = null,
-              eap = null, identity = null, password = null,
-              phase2 = null, privateKey =  null;
-
-        Field[] fields = WifiConfiguration.class.getFields();
-
-
-        for (Field tempField : fields) {
-            if (tempField.getName().trim().equals(INT_ANONYMOUS_IDENTITY)) {
-                anonymousId = tempField;
-                Log.d(TAG, "field " + anonymousId.getName() );
-            } else if (tempField.getName().trim().equals(INT_CA_CERT)) {
-                caCert = tempField;
-            } else if (tempField.getName().trim().equals(INT_CLIENT_CERT)) {
-                clientCert = tempField;
-                Log.d(TAG, "field " + clientCert.getName() );
-            } else if (tempField.getName().trim().equals(INT_EAP)) {
-                eap = tempField;
-                Log.d(TAG, "field " + eap.getName() );
-            } else if (tempField.getName().trim().equals(INT_IDENTITY)) {
-                identity = tempField;
-                Log.d(TAG, "field " + identity.getName() );
-            } else if (tempField.getName().trim().equals(INT_PASSWORD)) {
-                password = tempField;
-                Log.d(TAG, "field " + password.getName() );
-            } else if (tempField.getName().trim().equals(INT_PHASE2)) {
-                phase2 = tempField;
-                Log.d(TAG, "field " + phase2.getName() );
-
-            } else if (tempField.getName().trim().equals(INT_PRIVATE_KEY)) {
-                privateKey = tempField;
-            }
-        }
-
-
-        Method setValue = null;
-
-        for(Method m: enterpriseFieldClass.getMethods()) {
-            if(m.getName().trim().equals("setValue")) {
-                Log.d(TAG, "method " + m.getName() );
-                setValue = m;
-                break;
-            }
-        }
-
-        try {
-            // EAP
-            String eapmethod = mCredential.getType();
-            Log.d(TAG, "eapmethod:" + eapmethod);
-            setValue.invoke(eap.get(wfg), eapmethod);
-
-            // Username, password, EAP Phase 2
-            if ("TTLS".equals(eapmethod)) {
-                setValue.invoke(phase2.get(wfg), ENTERPRISE_PHASE2_MSCHAPV2);
-                setValue.invoke(identity.get(wfg), mCredential.getUserName());
-                setValue.invoke(password.get(wfg), mCredential.getPassword());
-                setValue.invoke(anonymousId.get(wfg), "anonymous@" + mCredential.getRealm());
-            }
-
-            // EAP CA Certificate
-            String cacertificate = null;
-            String rootCA = mCredential.getCaRootCertPath();
-            if (rootCA == null){
-                cacertificate = null;
-            } else {
-                cacertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.CA_CERTIFICATE + rootCA;
-            }
-            Log.d(TAG, "cacertificate:" + cacertificate);
-            setValue.invoke(caCert.get(wfg), cacertificate);
-
-            //User certificate
-            if ("TLS".equals(eapmethod)) {
-                String usercertificate = null;
-                String privatekey = null;
-                String clientCertPath = mCredential.getClientCertPath();
-                if (clientCertPath != null){
-                    privatekey = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_PRIVATE_KEY + clientCertPath;
-                    usercertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_CERTIFICATE + clientCertPath;
-                }
-                Log.d(TAG, "privatekey:" + privatekey);
-                Log.d(TAG, "usercertificate:" + usercertificate);
-                if (privatekey != null && usercertificate != null) {
-                    setValue.invoke(privateKey.get(wfg), privatekey);
-                    setValue.invoke(clientCert.get(wfg), usercertificate);
-                }
-            }
-        } catch (Exception e) {
-            Log.d(TAG, "createWifiConfiguration err:" + e);
-        }
-
-        return wfg;
-    }
-
-    /** {@inheritDoc} @hide */
-    public int compareTo(WifiPasspointPolicy another) {
-        Log.d(TAG, "this:" + this);
-        Log.d(TAG, "another:" + another);
-
-        if (another == null) {
-            return -1;
-        } else if (this.mIsHomeSp == true && another.isHomeSp() == false) {
-            //home sp priority is higher then roaming
-            Log.d(TAG, "compare HomeSP  first, this is HomeSP, another isn't");
-            return -1;
-        } else if ((this.mIsHomeSp == true && another.isHomeSp() == true)) {
-            Log.d(TAG, "both HomeSP");
-            //if both home sp, compare credential priority
-            if (this.mCredentialPriority < another.getCredentialPriority()) {
-                Log.d(TAG, "this priority is higher");
-                return -1;
-            } else if (this.mCredentialPriority == another.getCredentialPriority()) {
-                Log.d(TAG, "both priorities equal");
-                //if priority still the same, compare name(ssid)
-                if (this.mName.compareTo(another.mName) != 0) {
-                    Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName));
-                    return this.mName.compareTo(another.mName);
-                }
-                /**
-                 *if name still the same, compare credential
-                 *the device may has two more credentials(TLS,SIM..etc)
-                 *it can associate to one AP(same ssid). so we should compare by credential
-                 */
-                if (this.mCredential != null && another.mCredential != null) {
-                    if (this.mCredential.compareTo(another.mCredential) != 0) {
-                        Log.d(TAG,
-                                "compare mCredential return:" + this.mName.compareTo(another.mName));
-                        return this.mCredential.compareTo(another.mCredential);
-                    }
-                }
-            } else {
-                return 1;
-            }
-        } else if ((this.mIsHomeSp == false && another.isHomeSp() == false)) {
-            Log.d(TAG, "both RoamingSp");
-            //if both roaming sp, compare roaming priority(preferredRoamingPartnerList/<X+>/priority)
-            if (this.mRoamingPriority < another.getRoamingPriority()) {
-                Log.d(TAG, "this priority is higher");
-                return -1;
-            } else if (this.mRoamingPriority == another.getRoamingPriority()) {//priority equals, compare name
-                Log.d(TAG, "both priorities equal");
-                //if priority still the same, compare name(ssid)
-                if (this.mName.compareTo(another.mName) != 0) {
-                    Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName));
-                    return this.mName.compareTo(another.mName);
-                }
-                //if name still the same, compare credential
-                if (this.mCredential != null && another.mCredential != null) {
-                    if (this.mCredential.compareTo(another.mCredential) != 0) {
-                        Log.d(TAG,
-                                "compare mCredential return:"
-                                        + this.mCredential.compareTo(another.mCredential));
-                        return this.mCredential.compareTo(another.mCredential);
-                    }
-                }
-            } else {
-                return 1;
-            }
-        }
-
-        Log.d(TAG, "both policies equal");
-        return 0;
-    }
-
-    @Override
-    /** @hide */
-    public String toString() {
-        return "PasspointPolicy: name=" + mName + " CredentialPriority=" + mCredentialPriority +
-                " mRoamingPriority" + mRoamingPriority +
-                " ssid=" + mSsid + " restriction=" + mRestriction +
-                " ishomesp=" + mIsHomeSp + " Credential=" + mCredential;
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        // TODO
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public static final Creator<WifiPasspointPolicy> CREATOR =
-            new Creator<WifiPasspointPolicy>() {
-                @Override
-                public WifiPasspointPolicy createFromParcel(Parcel in) {
-                    return null;
-                }
-
-                @Override
-                public WifiPasspointPolicy[] newArray(int size) {
-                    return new WifiPasspointPolicy[size];
-                }
-            };
-}