Merge "Only skip unrecognized tags."
diff --git a/api/current.txt b/api/current.txt
index fee81f5c..578f808 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -505,7 +505,6 @@
     field public static final int dropDownWidth = 16843362; // 0x1010262
     field public static final int duplicateParentState = 16842985; // 0x10100e9
     field public static final int duration = 16843160; // 0x1010198
-    field public static final int durationScaleHint = 16844014; // 0x10104ee
     field public static final int dynamicResources = 16844019; // 0x10104f3
     field public static final int editTextBackground = 16843602; // 0x1010352
     field public static final int editTextColor = 16843601; // 0x1010351
@@ -996,6 +995,7 @@
     field public static final int readPermission = 16842759; // 0x1010007
     field public static final int recognitionService = 16843932; // 0x101049c
     field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
+    field public static final int removeBeforeMRelease = 16844014; // 0x10104ee
     field public static final int reparent = 16843964; // 0x10104bc
     field public static final int reparentWithOverlay = 16843965; // 0x10104bd
     field public static final int repeatCount = 16843199; // 0x10101bf
@@ -2868,7 +2868,6 @@
     method public void cancel();
     method public android.animation.Animator clone();
     method public void end();
-    method public long getDistanceBasedDuration();
     method public abstract long getDuration();
     method public android.animation.TimeInterpolator getInterpolator();
     method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
@@ -2882,16 +2881,12 @@
     method public void removePauseListener(android.animation.Animator.AnimatorPauseListener);
     method public void resume();
     method public abstract android.animation.Animator setDuration(long);
-    method public void setDurationScaleHint(int, android.content.res.Resources);
     method public abstract void setInterpolator(android.animation.TimeInterpolator);
     method public abstract void setStartDelay(long);
     method public void setTarget(java.lang.Object);
     method public void setupEndValues();
     method public void setupStartValues();
     method public void start();
-    field public static final int HINT_DISTANCE_DEFINED_IN_DP = 2; // 0x2
-    field public static final int HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE = 1; // 0x1
-    field public static final int HINT_NO_SCALE = 0; // 0x0
   }
 
   public static abstract interface Animator.AnimatorListener {
@@ -6132,6 +6127,7 @@
   }
 
   public final class UsageStatsManager {
+    method public boolean isAppIdle(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
     method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
     method public android.app.usage.UsageEvents queryEvents(long, long);
@@ -14746,7 +14742,8 @@
     field public static final int CHANNEL_IN_Y_AXIS = 4096; // 0x1000
     field public static final int CHANNEL_IN_Z_AXIS = 8192; // 0x2000
     field public static final int CHANNEL_OUT_5POINT1 = 252; // 0xfc
-    field public static final int CHANNEL_OUT_7POINT1 = 1020; // 0x3fc
+    field public static final deprecated int CHANNEL_OUT_7POINT1 = 1020; // 0x3fc
+    field public static final int CHANNEL_OUT_7POINT1_SURROUND = 6396; // 0x18fc
     field public static final int CHANNEL_OUT_BACK_CENTER = 1024; // 0x400
     field public static final int CHANNEL_OUT_BACK_LEFT = 64; // 0x40
     field public static final int CHANNEL_OUT_BACK_RIGHT = 128; // 0x80
@@ -14892,6 +14889,8 @@
     field public static final deprecated int NUM_STREAMS = 5; // 0x5
     field public static final java.lang.String PROPERTY_OUTPUT_FRAMES_PER_BUFFER = "android.media.property.OUTPUT_FRAMES_PER_BUFFER";
     field public static final java.lang.String PROPERTY_OUTPUT_SAMPLE_RATE = "android.media.property.OUTPUT_SAMPLE_RATE";
+    field public static final java.lang.String PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = "android.media.property.SUPPORT_MIC_NEAR_ULTRASOUND";
+    field public static final java.lang.String PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = "android.media.property.SUPPORT_SPEAKER_NEAR_ULTRASOUND";
     field public static final java.lang.String RINGER_MODE_CHANGED_ACTION = "android.media.RINGER_MODE_CHANGED";
     field public static final int RINGER_MODE_NORMAL = 2; // 0x2
     field public static final int RINGER_MODE_SILENT = 0; // 0x0
@@ -16392,10 +16391,13 @@
     method public final void release();
     method public void setAudioTrack(android.media.AudioTrack);
     method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
+    method public void setOnErrorListener(android.media.MediaSync.OnErrorListener, android.os.Handler);
     method public void setPlaybackRate(float, int);
     method public void setPlaybackSettings(android.media.PlaybackSettings);
     method public void setSurface(android.view.Surface);
     method public void setSyncSettings(android.media.SyncSettings);
+    field public static final int MEDIASYNC_ERROR_AUDIOTRACK_FAIL = 1; // 0x1
+    field public static final int MEDIASYNC_ERROR_SURFACE_FAIL = 2; // 0x2
     field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0
     field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2
     field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1
@@ -16403,7 +16405,11 @@
 
   public static abstract class MediaSync.Callback {
     ctor public MediaSync.Callback();
-    method public abstract void onReturnAudioBuffer(android.media.MediaSync, java.nio.ByteBuffer, int);
+    method public abstract void onAudioBufferConsumed(android.media.MediaSync, java.nio.ByteBuffer, int);
+  }
+
+  public static abstract interface MediaSync.OnErrorListener {
+    method public abstract void onError(android.media.MediaSync, int, int);
   }
 
   public class MediaSyncEvent {
@@ -16429,6 +16435,14 @@
     method public abstract void onAudioDeviceConnection();
   }
 
+  public abstract interface OnAudioRecordRoutingListener {
+    method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+  }
+
+  public abstract interface OnAudioTrackRoutingListener {
+    method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+  }
+
   public final class PlaybackSettings {
     ctor public PlaybackSettings();
     method public android.media.PlaybackSettings allowDefaults();
@@ -16447,14 +16461,6 @@
     field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
   }
 
-  public abstract interface OnAudioRecordRoutingListener {
-    method public abstract void onAudioRecordRouting(android.media.AudioRecord);
-  }
-
-  public abstract interface OnAudioTrackRoutingListener {
-    method public abstract void onAudioTrackRouting(android.media.AudioTrack);
-  }
-
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -17819,6 +17825,7 @@
     method public java.lang.String getRatingSystem();
     method public java.util.List<java.lang.String> getSubRatings();
     method public static android.media.tv.TvContentRating unflattenFromString(java.lang.String);
+    field public static final android.media.tv.TvContentRating UNRATED;
   }
 
   public final class TvContract {
@@ -18292,17 +18299,18 @@
     method public boolean bindProcessToNetwork(android.net.Network);
     method public android.net.Network getActiveNetwork();
     method public android.net.NetworkInfo getActiveNetworkInfo();
-    method public android.net.NetworkInfo[] getAllNetworkInfo();
+    method public deprecated android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.Network getBoundNetworkForProcess();
     method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
-    method public android.net.NetworkInfo getNetworkInfo(int);
+    method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
     method public static deprecated android.net.Network getProcessDefaultNetwork();
+    method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static boolean isNetworkTypeValid(int);
@@ -18310,6 +18318,7 @@
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public deprecated void reportBadNetwork(android.net.Network);
+    method public void reportCaptivePortalDismissed(android.net.Network, java.lang.String);
     method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
@@ -18317,8 +18326,10 @@
     method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
     method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+    field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
     field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+    field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -19279,8 +19290,6 @@
     field public java.util.BitSet allowedKeyManagement;
     field public java.util.BitSet allowedPairwiseCiphers;
     field public java.util.BitSet allowedProtocols;
-    field public int apBand;
-    field public int apChannel;
     field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
     field public boolean hiddenSSID;
     field public int networkId;
@@ -19349,6 +19358,7 @@
     method public java.lang.String getAnonymousIdentity();
     method public java.security.cert.X509Certificate getCaCertificate();
     method public java.security.cert.X509Certificate getClientCertificate();
+    method public java.lang.String getDomainSubjectMatch();
     method public int getEapMethod();
     method public java.lang.String getIdentity();
     method public java.lang.String getPassword();
@@ -19360,6 +19370,7 @@
     method public void setAnonymousIdentity(java.lang.String);
     method public void setCaCertificate(java.security.cert.X509Certificate);
     method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+    method public void setDomainSuffixMatch(java.lang.String);
     method public void setEapMethod(int);
     method public void setIdentity(java.lang.String);
     method public void setPassword(java.lang.String);
@@ -23400,6 +23411,7 @@
     method public final android.os.IBinder readStrongBinder();
     method public final void readTypedArray(T[], android.os.Parcelable.Creator<T>);
     method public final void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
+    method public final T readTypedObject(android.os.Parcelable.Creator<T>);
     method public final java.lang.Object readValue(java.lang.ClassLoader);
     method public final void recycle();
     method public final void setDataCapacity(int);
@@ -23444,6 +23456,7 @@
     method public final void writeStrongInterface(android.os.IInterface);
     method public final void writeTypedArray(T[], int);
     method public final void writeTypedList(java.util.List<T>);
+    method public final void writeTypedObject(T, int);
     method public final void writeValue(java.lang.Object);
     field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
   }
@@ -28440,13 +28453,6 @@
 
 package android.security {
 
-  public class CryptoOperationException extends java.lang.RuntimeException {
-    ctor public CryptoOperationException();
-    ctor public CryptoOperationException(java.lang.String);
-    ctor public CryptoOperationException(java.lang.String, java.lang.Throwable);
-    ctor public CryptoOperationException(java.lang.Throwable);
-  }
-
   public class EcIesParameterSpec implements java.security.spec.AlgorithmParameterSpec {
     method public int getDemCipherKeySize();
     method public java.lang.String getDemCipherTransformation();
@@ -28503,7 +28509,7 @@
     ctor public KeyChainException(java.lang.Throwable);
   }
 
-  public class KeyExpiredException extends android.security.CryptoOperationException {
+  public class KeyExpiredException extends java.security.InvalidKeyException {
     ctor public KeyExpiredException();
     ctor public KeyExpiredException(java.lang.String);
     ctor public KeyExpiredException(java.lang.String, java.lang.Throwable);
@@ -28545,7 +28551,7 @@
     method public android.security.KeyGeneratorSpec.Builder setUserAuthenticators(int);
   }
 
-  public class KeyNotYetValidException extends android.security.CryptoOperationException {
+  public class KeyNotYetValidException extends java.security.InvalidKeyException {
     ctor public KeyNotYetValidException();
     ctor public KeyNotYetValidException(java.lang.String);
     ctor public KeyNotYetValidException(java.lang.String, java.lang.Throwable);
@@ -28693,12 +28699,12 @@
     method public boolean isCleartextTrafficPermitted();
   }
 
-  public class NewFingerprintEnrolledException extends android.security.CryptoOperationException {
+  public class NewFingerprintEnrolledException extends java.security.InvalidKeyException {
     ctor public NewFingerprintEnrolledException();
     ctor public NewFingerprintEnrolledException(java.lang.String);
   }
 
-  public class UserNotAuthenticatedException extends android.security.CryptoOperationException {
+  public class UserNotAuthenticatedException extends java.security.InvalidKeyException {
     ctor public UserNotAuthenticatedException();
     ctor public UserNotAuthenticatedException(java.lang.String);
     ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
@@ -28911,6 +28917,7 @@
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
+    method public final void setNotificationsShown(java.lang.String[]);
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -30215,6 +30222,7 @@
     method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method public android.telecom.Connection getPrimaryConnection();
     method public final int getState();
+    method public final android.telecom.StatusHints getStatusHints();
     method public void onAudioStateChanged(android.telecom.AudioState);
     method public void onConnectionAdded(android.telecom.Connection);
     method public void onDisconnect();
@@ -30233,6 +30241,7 @@
     method public final void setConnectionCapabilities(int);
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setOnHold();
+    method public final void setStatusHints(android.telecom.StatusHints);
     field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
   }
 
@@ -30423,6 +30432,7 @@
   public static abstract class InCallService.VideoCall {
     ctor public InCallService.VideoCall();
     method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback);
+    method public abstract void unregisterCallback();
     method public abstract void requestCallDataUsage();
     method public abstract void requestCameraCapabilities();
     method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
@@ -30600,6 +30610,7 @@
     method public void cancelMissedCallsNotification();
     method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
     method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
+    method public java.lang.String getDefaultDialerPackage();
     method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
     method public java.lang.String getLine1Number(android.telecom.PhoneAccountHandle);
     method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -30614,6 +30625,7 @@
     method public void showInCallScreen(boolean);
     method public void silenceRinger();
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
+    field public static final java.lang.String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
@@ -30624,6 +30636,7 @@
     field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+    field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
diff --git a/api/system-current.txt b/api/system-current.txt
index 137bb19..2ea68c6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -578,7 +578,6 @@
     field public static final int dropDownWidth = 16843362; // 0x1010262
     field public static final int duplicateParentState = 16842985; // 0x10100e9
     field public static final int duration = 16843160; // 0x1010198
-    field public static final int durationScaleHint = 16844014; // 0x10104ee
     field public static final int dynamicResources = 16844019; // 0x10104f3
     field public static final int editTextBackground = 16843602; // 0x1010352
     field public static final int editTextColor = 16843601; // 0x1010351
@@ -1069,6 +1068,7 @@
     field public static final int readPermission = 16842759; // 0x1010007
     field public static final int recognitionService = 16843932; // 0x101049c
     field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
+    field public static final int removeBeforeMRelease = 16844014; // 0x10104ee
     field public static final int reparent = 16843964; // 0x10104bc
     field public static final int reparentWithOverlay = 16843965; // 0x10104bd
     field public static final int repeatCount = 16843199; // 0x10101bf
@@ -2948,7 +2948,6 @@
     method public void cancel();
     method public android.animation.Animator clone();
     method public void end();
-    method public long getDistanceBasedDuration();
     method public abstract long getDuration();
     method public android.animation.TimeInterpolator getInterpolator();
     method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
@@ -2962,16 +2961,12 @@
     method public void removePauseListener(android.animation.Animator.AnimatorPauseListener);
     method public void resume();
     method public abstract android.animation.Animator setDuration(long);
-    method public void setDurationScaleHint(int, android.content.res.Resources);
     method public abstract void setInterpolator(android.animation.TimeInterpolator);
     method public abstract void setStartDelay(long);
     method public void setTarget(java.lang.Object);
     method public void setupEndValues();
     method public void setupStartValues();
     method public void start();
-    field public static final int HINT_DISTANCE_DEFINED_IN_DP = 2; // 0x2
-    field public static final int HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE = 1; // 0x1
-    field public static final int HINT_NO_SCALE = 0; // 0x0
   }
 
   public static abstract interface Animator.AnimatorListener {
@@ -6320,6 +6315,7 @@
   }
 
   public final class UsageStatsManager {
+    method public boolean isAppIdle(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
     method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
     method public android.app.usage.UsageEvents queryEvents(long, long);
@@ -15947,7 +15943,8 @@
     field public static final int CHANNEL_IN_Y_AXIS = 4096; // 0x1000
     field public static final int CHANNEL_IN_Z_AXIS = 8192; // 0x2000
     field public static final int CHANNEL_OUT_5POINT1 = 252; // 0xfc
-    field public static final int CHANNEL_OUT_7POINT1 = 1020; // 0x3fc
+    field public static final deprecated int CHANNEL_OUT_7POINT1 = 1020; // 0x3fc
+    field public static final int CHANNEL_OUT_7POINT1_SURROUND = 6396; // 0x18fc
     field public static final int CHANNEL_OUT_BACK_CENTER = 1024; // 0x400
     field public static final int CHANNEL_OUT_BACK_LEFT = 64; // 0x40
     field public static final int CHANNEL_OUT_BACK_RIGHT = 128; // 0x80
@@ -16102,6 +16099,8 @@
     field public static final deprecated int NUM_STREAMS = 5; // 0x5
     field public static final java.lang.String PROPERTY_OUTPUT_FRAMES_PER_BUFFER = "android.media.property.OUTPUT_FRAMES_PER_BUFFER";
     field public static final java.lang.String PROPERTY_OUTPUT_SAMPLE_RATE = "android.media.property.OUTPUT_SAMPLE_RATE";
+    field public static final java.lang.String PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = "android.media.property.SUPPORT_MIC_NEAR_ULTRASOUND";
+    field public static final java.lang.String PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = "android.media.property.SUPPORT_SPEAKER_NEAR_ULTRASOUND";
     field public static final java.lang.String RINGER_MODE_CHANGED_ACTION = "android.media.RINGER_MODE_CHANGED";
     field public static final int RINGER_MODE_NORMAL = 2; // 0x2
     field public static final int RINGER_MODE_SILENT = 0; // 0x0
@@ -17608,10 +17607,13 @@
     method public final void release();
     method public void setAudioTrack(android.media.AudioTrack);
     method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
+    method public void setOnErrorListener(android.media.MediaSync.OnErrorListener, android.os.Handler);
     method public void setPlaybackRate(float, int);
     method public void setPlaybackSettings(android.media.PlaybackSettings);
     method public void setSurface(android.view.Surface);
     method public void setSyncSettings(android.media.SyncSettings);
+    field public static final int MEDIASYNC_ERROR_AUDIOTRACK_FAIL = 1; // 0x1
+    field public static final int MEDIASYNC_ERROR_SURFACE_FAIL = 2; // 0x2
     field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0
     field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2
     field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1
@@ -17619,7 +17621,11 @@
 
   public static abstract class MediaSync.Callback {
     ctor public MediaSync.Callback();
-    method public abstract void onReturnAudioBuffer(android.media.MediaSync, java.nio.ByteBuffer, int);
+    method public abstract void onAudioBufferConsumed(android.media.MediaSync, java.nio.ByteBuffer, int);
+  }
+
+  public static abstract interface MediaSync.OnErrorListener {
+    method public abstract void onError(android.media.MediaSync, int, int);
   }
 
   public class MediaSyncEvent {
@@ -17645,6 +17651,14 @@
     method public abstract void onAudioDeviceConnection();
   }
 
+  public abstract interface OnAudioRecordRoutingListener {
+    method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+  }
+
+  public abstract interface OnAudioTrackRoutingListener {
+    method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+  }
+
   public final class PlaybackSettings {
     ctor public PlaybackSettings();
     method public android.media.PlaybackSettings allowDefaults();
@@ -17663,14 +17677,6 @@
     field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
   }
 
-  public abstract interface OnAudioRecordRoutingListener {
-    method public abstract void onAudioRecordRouting(android.media.AudioRecord);
-  }
-
-  public abstract interface OnAudioTrackRoutingListener {
-    method public abstract void onAudioTrackRouting(android.media.AudioTrack);
-  }
-
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -19104,6 +19110,7 @@
     method public java.lang.String getRatingSystem();
     method public java.util.List<java.lang.String> getSubRatings();
     method public static android.media.tv.TvContentRating unflattenFromString(java.lang.String);
+    field public static final android.media.tv.TvContentRating UNRATED;
   }
 
   public final class TvContentRatingSystemInfo implements android.os.Parcelable {
@@ -19751,17 +19758,18 @@
     method public boolean bindProcessToNetwork(android.net.Network);
     method public android.net.Network getActiveNetwork();
     method public android.net.NetworkInfo getActiveNetworkInfo();
-    method public android.net.NetworkInfo[] getAllNetworkInfo();
+    method public deprecated android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.Network getBoundNetworkForProcess();
     method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
-    method public android.net.NetworkInfo getNetworkInfo(int);
+    method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
     method public static deprecated android.net.Network getProcessDefaultNetwork();
+    method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static boolean isNetworkTypeValid(int);
@@ -19769,6 +19777,7 @@
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public deprecated void reportBadNetwork(android.net.Network);
+    method public void reportCaptivePortalDismissed(android.net.Network, java.lang.String);
     method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
@@ -19776,8 +19785,10 @@
     method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
     method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+    field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
     field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+    field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -20978,8 +20989,6 @@
     field public java.util.BitSet allowedKeyManagement;
     field public java.util.BitSet allowedPairwiseCiphers;
     field public java.util.BitSet allowedProtocols;
-    field public int apBand;
-    field public int apChannel;
     field public java.lang.String creatorName;
     field public int creatorUid;
     field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
@@ -21070,6 +21079,7 @@
     method public java.lang.String getAnonymousIdentity();
     method public java.security.cert.X509Certificate getCaCertificate();
     method public java.security.cert.X509Certificate getClientCertificate();
+    method public java.lang.String getDomainSubjectMatch();
     method public int getEapMethod();
     method public java.lang.String getIdentity();
     method public java.lang.String getPassword();
@@ -21081,6 +21091,7 @@
     method public void setAnonymousIdentity(java.lang.String);
     method public void setCaCertificate(java.security.cert.X509Certificate);
     method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+    method public void setDomainSuffixMatch(java.lang.String);
     method public void setEapMethod(int);
     method public void setIdentity(java.lang.String);
     method public void setPassword(java.lang.String);
@@ -25289,6 +25300,7 @@
     method public final android.os.IBinder readStrongBinder();
     method public final void readTypedArray(T[], android.os.Parcelable.Creator<T>);
     method public final void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
+    method public final T readTypedObject(android.os.Parcelable.Creator<T>);
     method public final java.lang.Object readValue(java.lang.ClassLoader);
     method public final void recycle();
     method public final void setDataCapacity(int);
@@ -25333,6 +25345,7 @@
     method public final void writeStrongInterface(android.os.IInterface);
     method public final void writeTypedArray(T[], int);
     method public final void writeTypedList(java.util.List<T>);
+    method public final void writeTypedObject(T, int);
     method public final void writeValue(java.lang.Object);
     field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
   }
@@ -30444,13 +30457,6 @@
 
 package android.security {
 
-  public class CryptoOperationException extends java.lang.RuntimeException {
-    ctor public CryptoOperationException();
-    ctor public CryptoOperationException(java.lang.String);
-    ctor public CryptoOperationException(java.lang.String, java.lang.Throwable);
-    ctor public CryptoOperationException(java.lang.Throwable);
-  }
-
   public class EcIesParameterSpec implements java.security.spec.AlgorithmParameterSpec {
     method public int getDemCipherKeySize();
     method public java.lang.String getDemCipherTransformation();
@@ -30507,7 +30513,7 @@
     ctor public KeyChainException(java.lang.Throwable);
   }
 
-  public class KeyExpiredException extends android.security.CryptoOperationException {
+  public class KeyExpiredException extends java.security.InvalidKeyException {
     ctor public KeyExpiredException();
     ctor public KeyExpiredException(java.lang.String);
     ctor public KeyExpiredException(java.lang.String, java.lang.Throwable);
@@ -30549,7 +30555,7 @@
     method public android.security.KeyGeneratorSpec.Builder setUserAuthenticators(int);
   }
 
-  public class KeyNotYetValidException extends android.security.CryptoOperationException {
+  public class KeyNotYetValidException extends java.security.InvalidKeyException {
     ctor public KeyNotYetValidException();
     ctor public KeyNotYetValidException(java.lang.String);
     ctor public KeyNotYetValidException(java.lang.String, java.lang.Throwable);
@@ -30697,12 +30703,12 @@
     method public boolean isCleartextTrafficPermitted();
   }
 
-  public class NewFingerprintEnrolledException extends android.security.CryptoOperationException {
+  public class NewFingerprintEnrolledException extends java.security.InvalidKeyException {
     ctor public NewFingerprintEnrolledException();
     ctor public NewFingerprintEnrolledException(java.lang.String);
   }
 
-  public class UserNotAuthenticatedException extends android.security.CryptoOperationException {
+  public class UserNotAuthenticatedException extends java.security.InvalidKeyException {
     ctor public UserNotAuthenticatedException();
     ctor public UserNotAuthenticatedException(java.lang.String);
     ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
@@ -30956,6 +30962,7 @@
     method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException;
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
+    method public final void setNotificationsShown(java.lang.String[]);
     method public final void setOnNotificationPostedTrim(int);
     method public void unregisterAsSystemService() throws android.os.RemoteException;
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
@@ -32326,6 +32333,7 @@
     method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method public android.telecom.Connection getPrimaryConnection();
     method public final int getState();
+    method public final android.telecom.StatusHints getStatusHints();
     method public void onAudioStateChanged(android.telecom.AudioState);
     method public void onConnectionAdded(android.telecom.Connection);
     method public void onDisconnect();
@@ -32344,6 +32352,7 @@
     method public final void setConnectionCapabilities(int);
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setOnHold();
+    method public final void setStatusHints(android.telecom.StatusHints);
     field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
   }
 
@@ -32537,6 +32546,7 @@
   public static abstract class InCallService.VideoCall {
     ctor public InCallService.VideoCall();
     method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback);
+    method public abstract void unregisterCallback();
     method public abstract void requestCallDataUsage();
     method public abstract void requestCameraCapabilities();
     method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
@@ -32744,8 +32754,9 @@
     method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
     method public int getCallState();
     method public android.telecom.PhoneAccountHandle getConnectionManager();
+    method public java.lang.String getDefaultDialerPackage();
     method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
-    method public android.content.ComponentName getDefaultPhoneApp();
+    method public deprecated android.content.ComponentName getDefaultPhoneApp();
     method public java.lang.String getLine1Number(android.telecom.PhoneAccountHandle);
     method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
@@ -32764,6 +32775,7 @@
     method public void showInCallScreen(boolean);
     method public void silenceRinger();
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
+    field public static final java.lang.String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
     field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
@@ -32776,6 +32788,7 @@
     field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+    field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 8ba2a5a..219d35b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -142,6 +142,8 @@
                 "       am task resizeable <TASK_ID> [true|false]\n" +
                 "       am task resize <TASK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
                 "       am get-config\n" +
+                "       am set-idle [--user <USER_ID>] <PACKAGE> true|false\n" +
+                "       am get-idle [--user <USER_ID>] <PACKAGE>\n" +
                 "\n" +
                 "am start: start an Activity.  Options are:\n" +
                 "    -D: enable debugging\n" +
@@ -282,6 +284,11 @@
                 "am get-config: retrieve the configuration and any recent configurations\n" +
                 "  of the device\n" +
                 "\n" +
+                "am set-idle: sets the idle state of an app\n" +
+                "\n" +
+                "am get-idle: returns the idle state of an app\n" +
+                "\n" +
+                "\n" +
                 "<INTENT> specifications include these flags and arguments:\n" +
                 "    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
                 "    [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
@@ -388,6 +395,10 @@
             runTask();
         } else if (op.equals("get-config")) {
             runGetConfig();
+        } else if (op.equals("set-idle")) {
+            runSetIdle();
+        } else if (op.equals("get-idle")) {
+            runGetIdle();
         } else {
             showError("Error: unknown command '" + op + "'");
         }
@@ -2019,6 +2030,46 @@
         }
     }
 
+    private void runSetIdle() throws Exception {
+        int userId = UserHandle.USER_OWNER;
+
+        String opt;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = parseUserArg(nextArgRequired());
+            } else {
+                System.err.println("Error: Unknown option: " + opt);
+                return;
+            }
+        }
+        String packageName = nextArgRequired();
+        String value = nextArgRequired();
+
+        IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+                Context.USAGE_STATS_SERVICE));
+        usm.setAppIdle(packageName, Boolean.parseBoolean(value), userId);
+    }
+
+    private void runGetIdle() throws Exception {
+        int userId = UserHandle.USER_OWNER;
+
+        String opt;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = parseUserArg(nextArgRequired());
+            } else {
+                System.err.println("Error: Unknown option: " + opt);
+                return;
+            }
+        }
+        String packageName = nextArgRequired();
+
+        IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+                Context.USAGE_STATS_SERVICE));
+        boolean isIdle = usm.isAppIdle(packageName, userId);
+        System.out.println("Idle=" + isIdle);
+    }
+
     /**
      * Open the given file for sending into the system process. This verifies
      * with SELinux that the system will have access to the file.
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index b84b1e2..39de1dc7 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -30,7 +30,6 @@
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageManager;
-import android.content.pm.IPackageMoveObserver;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
@@ -237,8 +236,12 @@
             return runForceDexOpt();
         }
 
-        if ("move".equals(op)) {
-            return runMove();
+        if ("move-package".equals(op)) {
+            return runMovePackage();
+        }
+
+        if ("move-primary-storage".equals(op)) {
+            return runMovePrimaryStorage();
         }
 
         try {
@@ -1285,7 +1288,7 @@
         }
     }
 
-    public int runMove() {
+    public int runMovePackage() {
         final String packageName = nextArg();
         String volumeUuid = nextArg();
         if ("internal".equals(volumeUuid)) {
@@ -1313,6 +1316,33 @@
         }
     }
 
+    public int runMovePrimaryStorage() {
+        String volumeUuid = nextArg();
+        if ("internal".equals(volumeUuid)) {
+            volumeUuid = null;
+        }
+
+        try {
+            final int moveId = mPm.movePrimaryStorage(volumeUuid);
+
+            int status = mPm.getMoveStatus(moveId);
+            while (!PackageManager.isMoveStatusFinished(status)) {
+                SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
+                status = mPm.getMoveStatus(moveId);
+            }
+
+            if (status == PackageManager.MOVE_SUCCEEDED) {
+                System.out.println("Success");
+                return 0;
+            } else {
+                System.err.println("Failure [" + status + "]");
+                return 1;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
     private int runUninstall() throws RemoteException {
         int flags = 0;
         int userId = UserHandle.USER_ALL;
@@ -1860,7 +1890,8 @@
         System.err.println("       pm install-abandon SESSION_ID");
         System.err.println("       pm uninstall [-k] [--user USER_ID] PACKAGE");
         System.err.println("       pm set-installer PACKAGE INSTALLER");
-        System.err.println("       pm move PACKAGE [internal|UUID]");
+        System.err.println("       pm move-package PACKAGE [internal|UUID]");
+        System.err.println("       pm move-primary-storage [internal|UUID]");
         System.err.println("       pm clear [--user USER_ID] PACKAGE");
         System.err.println("       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 02a329d..da48709 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -16,12 +16,7 @@
 
 package android.animation;
 
-import android.content.res.Configuration;
 import android.content.res.ConstantState;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.animation.AnimationUtils;
 
 import java.util.ArrayList;
 
@@ -30,29 +25,6 @@
  * started, ended, and have <code>AnimatorListeners</code> added to them.
  */
 public abstract class Animator implements Cloneable {
-    /**
-     * Set this hint when duration for the animation does not need to be scaled. By default, no
-     * scaling is applied to the duration.
-     */
-    public static final int HINT_NO_SCALE = 0;
-
-    /**
-     * Set this scale hint (using {@link #setDurationScaleHint(int, Resources)} when the animation's
-     * moving distance is proportional to the screen size. (e.g. a view coming in from the bottom of
-     * the screen to top/center). With this scale hint set, the animation duration will be
-     * automatically scaled based on screen size.
-     */
-    public static final int HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE = 1;
-
-    /**
-     * Set this scale hint (using {@link #setDurationScaleHint(int, Resources)}) if the animation
-     * has pre-defined moving distance in dp that does not vary from device to device. This is
-     * extremely useful when the animation needs to run on both phones/tablets and TV, because TV
-     * has inflated dp and therefore will have a longer visual arc for the same animation than on
-     * the phone. This hint is used to calculate a scaling factor to compensate for different
-     * visual arcs while maintaining the same angular velocity for the animation.
-     */
-    public static final int HINT_DISTANCE_DEFINED_IN_DP = 2;
 
     /**
      * The set of listeners to be sent events through the life of an animation.
@@ -83,24 +55,6 @@
     private AnimatorConstantState mConstantState;
 
     /**
-     * Scaling factor for an animation that moves across the whole screen.
-     */
-    float mScreenSizeBasedDurationScale = 1.0f;
-
-    /**
-     * Scaling factor for an animation that is defined to move the same amount of dp across all
-     * devices.
-     */
-    float mDpBasedDurationScale = 1.0f;
-
-    /**
-     * By default, the scaling assumes the animation moves across the entire screen.
-     */
-    int mDurationScaleHint = HINT_NO_SCALE;
-
-    private final static boolean ANIM_DEBUG = false;
-
-    /**
      * Starts this animation. If the animation has a nonzero startDelay, the animation will start
      * running after that delay elapses. A non-delayed animation will have its initial
      * value(s) set immediately, followed by calls to
@@ -230,78 +184,6 @@
     public abstract long getDuration();
 
     /**
-     * Hints how duration scaling factor should be calculated. The duration will not be scaled when
-     * hint is set to {@link #HINT_NO_SCALE}. Otherwise, the duration will be automatically scaled
-     * per device to achieve the same look and feel across different devices. In order to do
-     * that, the same angular velocity of the animation will be needed on different devices in
-     * users' field of view. Therefore, the duration scale factor is determined by the ratio of the
-     * angular movement on current devices to that on the baseline device (i.e. Nexus 5).
-     *
-     * @param hint an indicator on how the animation is defined. The hint could be
-     *             {@link #HINT_NO_SCALE}, {@link #HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE} or
-     *             {@link #HINT_DISTANCE_DEFINED_IN_DP}.
-     * @param res The resources {@see android.content.res.Resources} for getting display metrics
-     */
-    public void setDurationScaleHint(int hint, Resources res) {
-        if (ANIM_DEBUG) {
-            Log.d("ANIM_DEBUG", "distance based duration hint: " + hint);
-        }
-        if (hint == mDurationScaleHint) {
-            return;
-        }
-        mDurationScaleHint = hint;
-        if (hint != HINT_NO_SCALE) {
-            int uiMode = res.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK;
-            DisplayMetrics metrics = res.getDisplayMetrics();
-            float width = metrics.widthPixels / metrics.xdpi;
-            float height = metrics.heightPixels / metrics.ydpi;
-            float viewingDistance = AnimationUtils.getViewingDistance(width, height, uiMode);
-            if (ANIM_DEBUG) {
-                Log.d("ANIM_DEBUG", "width, height, viewing distance, uimode: "
-                        + width + ", " + height + ", " + viewingDistance + ", " + uiMode);
-            }
-            mScreenSizeBasedDurationScale = AnimationUtils
-                    .getScreenSizeBasedDurationScale(width, height, viewingDistance);
-            mDpBasedDurationScale = AnimationUtils.getDpBasedDurationScale(
-                    metrics.density, metrics.xdpi, viewingDistance);
-            if (ANIM_DEBUG) {
-                Log.d("ANIM_DEBUG", "screen based scale, dp based scale: " +
-                        mScreenSizeBasedDurationScale + ", " + mDpBasedDurationScale);
-            }
-        }
-    }
-
-    // Copies duration scale hint and scaling factors to the new animation.
-    void copyDurationScaleInfoTo(Animator anim) {
-        anim.mDurationScaleHint = mDurationScaleHint;
-        anim.mScreenSizeBasedDurationScale = mScreenSizeBasedDurationScale;
-        anim.mDpBasedDurationScale = mDpBasedDurationScale;
-    }
-
-    /**
-     * @return The scaled duration calculated based on distance of movement (as defined by the
-     * animation) and perceived velocity (derived from the duration set on the animation for
-     * baseline device)
-     */
-    public long getDistanceBasedDuration() {
-        return (long) (getDuration() * getDistanceBasedDurationScale());
-    }
-
-    /**
-     * @return scaling factor of duration based on the duration scale hint. A scaling factor of 1
-     * means no scaling will be applied to the duration.
-     */
-    float getDistanceBasedDurationScale() {
-        if (mDurationScaleHint == HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE) {
-            return mScreenSizeBasedDurationScale;
-        } else if (mDurationScaleHint == HINT_DISTANCE_DEFINED_IN_DP) {
-            return mDpBasedDurationScale;
-        } else {
-            return 1f;
-        }
-    }
-
-    /**
      * The time interpolator used in calculating the elapsed fraction of the
      * animation. The interpolator determines whether the animation runs with
      * linear or non-linear motion, such as acceleration and deceleration. The
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index e47d017..427ecce 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -70,13 +70,6 @@
     private static final int VALUE_TYPE_COLOR       = 3;
     private static final int VALUE_TYPE_UNDEFINED   = 4;
 
-    /**
-     * Enum values used in XML attributes to indicate the duration scale hint.
-     */
-    private static final int HINT_NO_SCALE                  = 0;
-    private static final int HINT_PROPORTIONAL_TO_SCREEN    = 1;
-    private static final int HINT_DEFINED_IN_DP             = 2;
-
     private static final boolean DBG_ANIMATOR_INFLATER = false;
 
     // used to calculate changing configs for resource references
@@ -698,9 +691,6 @@
                 int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER);
                 createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering,
                         pixelSize);
-                final int hint = a.getInt(R.styleable.AnimatorSet_durationScaleHint,
-                        HINT_NO_SCALE);
-                anim.setDurationScaleHint(hint, res);
                 a.recycle();
             } else if (name.equals("propertyValuesHolder")) {
                 PropertyValuesHolder[] values = loadValues(res, theme, parser,
@@ -1065,9 +1055,6 @@
             anim.setInterpolator(interpolator);
         }
 
-        final int hint = arrayAnimator.getInt(R.styleable.Animator_durationScaleHint,
-                HINT_NO_SCALE);
-        anim.setDurationScaleHint(hint, res);
         arrayAnimator.recycle();
         if (arrayObjectAnimator != null) {
             arrayObjectAnimator.recycle();
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index f6ad847..6503d89 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -519,7 +519,6 @@
 
         for (Node node : mNodes) {
             node.animation.setAllowRunningAsynchronously(false);
-            copyDurationScaleInfoTo(node.animation);
         }
 
         if (mDuration >= 0) {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 292507b..a455f8b 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -17,12 +17,9 @@
 package android.animation;
 
 import android.annotation.CallSuper;
-import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.os.Looper;
 import android.os.Trace;
 import android.util.AndroidRuntimeException;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.animation.AccelerateDecelerateInterpolator;
@@ -564,7 +561,7 @@
     }
 
     private void updateScaledDuration() {
-        mDuration = (long)(mUnscaledDuration * sDurationScale * getDistanceBasedDurationScale());
+        mDuration = (long)(mUnscaledDuration * sDurationScale);
     }
 
     /**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ab5f811..9bad9bb 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -255,23 +255,18 @@
         }
     }
 
-    static final class AcquiringProviderRecord {
-        IActivityManager.ContentProviderHolder holder;
-        boolean acquiring = true;
-        int requests = 1;
-        // Set if there was a runtime exception when trying to acquire the provider.
-        RuntimeException runtimeException = null;
-    }
-
     // The lock of mProviderMap protects the following variables.
-    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<>();
-    final ArrayMap<ProviderKey, AcquiringProviderRecord> mAcquiringProviderMap = new ArrayMap<>();
-    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap = new ArrayMap<>();
-    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders = new ArrayMap<>();
-    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName = new ArrayMap<>();
+    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
+        = new ArrayMap<ProviderKey, ProviderClientRecord>();
+    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
+        = new ArrayMap<IBinder, ProviderRefCount>();
+    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
+        = new ArrayMap<IBinder, ProviderClientRecord>();
+    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
+            = new ArrayMap<ComponentName, ProviderClientRecord>();
 
     final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
-            = new ArrayMap<>();
+        = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
 
     final GcIdler mGcIdler = new GcIdler();
     boolean mGcIdlerScheduled = false;
@@ -351,7 +346,7 @@
         }
     }
 
-    static final class ProviderClientRecord {
+    final class ProviderClientRecord {
         final String[] mNames;
         final IContentProvider mProvider;
         final ContentProvider mLocalProvider;
@@ -4716,74 +4711,23 @@
 
     public final IContentProvider acquireProvider(
             Context c, String auth, int userId, boolean stable) {
-        final ProviderKey key = new ProviderKey(auth, userId);
-        final IContentProvider provider = acquireExistingProvider(c, key, stable);
+        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
         if (provider != null) {
             return provider;
         }
-        AcquiringProviderRecord r;
-        boolean first = false;
-        synchronized (mAcquiringProviderMap) {
-            r = mAcquiringProviderMap.get(key);
-            if (r == null) {
-                r = new AcquiringProviderRecord();
-                mAcquiringProviderMap.put(key, r);
-                first = true;
-            } else {
-                r.requests++;
-            }
-        }
 
+        // There is a possible race here.  Another thread may try to acquire
+        // the same provider at the same time.  When this happens, we want to ensure
+        // that the first one wins.
+        // Note that we cannot hold the lock while acquiring and installing the
+        // provider since it might take a long time to run and it could also potentially
+        // be re-entrant in the case where the provider is in the same process.
         IActivityManager.ContentProviderHolder holder = null;
         try {
-            if (first) {
-                // Multiple threads may try to acquire the same provider at the same time.
-                // When this happens, we only let the first one really gets provider.
-                // Other threads just wait for its result.
-                // Note that we cannot hold the lock while acquiring and installing the
-                // provider since it might take a long time to run and it could also potentially
-                // be re-entrant in the case where the provider is in the same process.
-                holder = ActivityManagerNative.getDefault().getContentProvider(
-                        getApplicationThread(), auth, userId, stable);
-            } else {
-                synchronized (r) {
-                    while (r.acquiring) {
-                        try {
-                            r.wait();
-                        } catch (InterruptedException e) {
-                        }
-                    }
-                    holder = r.holder;
-                }
-            }
+            holder = ActivityManagerNative.getDefault().getContentProvider(
+                    getApplicationThread(), auth, userId, stable);
         } catch (RemoteException ex) {
-        } catch (RuntimeException e) {
-            synchronized (r) {
-                r.runtimeException = e;
-            }
-        } finally {
-            if (first) {
-                synchronized (r) {
-                    r.holder = holder;
-                    r.acquiring = false;
-                    r.notifyAll();
-                }
-            }
-
-            synchronized (mAcquiringProviderMap) {
-                if (--r.requests == 0) {
-                    mAcquiringProviderMap.remove(key);
-                }
-            }
-
-            if (r.runtimeException != null) {
-                // Was set when the first thread tried to acquire the provider,
-                // but we should make sure it is thrown for all threads trying to
-                // acquire the provider.
-                throw r.runtimeException;
-            }
         }
-
         if (holder == null) {
             Slog.e(TAG, "Failed to find provider info for " + auth);
             return null;
@@ -4866,12 +4810,8 @@
 
     public final IContentProvider acquireExistingProvider(
             Context c, String auth, int userId, boolean stable) {
-        return acquireExistingProvider(c, new ProviderKey(auth, userId), stable);
-    }
-
-    final IContentProvider acquireExistingProvider(
-            Context c, ProviderKey key, boolean stable) {
         synchronized (mProviderMap) {
+            final ProviderKey key = new ProviderKey(auth, userId);
             final ProviderClientRecord pr = mProviderMap.get(key);
             if (pr == null) {
                 return null;
@@ -4882,7 +4822,7 @@
             if (!jBinder.isBinderAlive()) {
                 // The hosting process of the provider has died; we can't
                 // use this one.
-                Log.i(TAG, "Acquiring provider " + key.authority + " for user " +  key.userId
+                Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
                         + ": existing object's process dead");
                 handleUnstableProviderDiedLocked(jBinder, true);
                 return null;
@@ -5204,12 +5144,18 @@
                     if (DEBUG_PROVIDER) {
                         Slog.v(TAG, "installProvider: lost the race, updating ref count");
                     }
-                    // The provider has already been installed, so we need
-                    // to increase reference count to the existing one, but
-                    // only if release is needed (that is, it is not running
-                    // in the system process or local to the process).
+                    // We need to transfer our new reference to the existing
+                    // ref count, releasing the old one...  but only if
+                    // release is needed (that is, it is not running in the
+                    // system process).
                     if (!noReleaseNeeded) {
                         incProviderRefLocked(prc, stable);
+                        try {
+                            ActivityManagerNative.getDefault().removeContentProvider(
+                                    holder.connection, stable);
+                        } catch (RemoteException e) {
+                            //do nothing content provider object is dead any way
+                        }
                     }
                 } else {
                     ProviderClientRecord client = installProviderAuthoritiesLocked(
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 10f5960..16a2430 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1556,6 +1556,7 @@
         }
     }
 
+    @Override
     public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() {
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final String volumeUuid = storage.getPrimaryStorageUuid();
@@ -1568,6 +1569,7 @@
         }
     }
 
+    @Override
     public @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() {
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final VolumeInfo currentVol = getPrimaryStorageCurrentVolume();
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e275df04..ac8d5d8 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -67,6 +67,8 @@
     void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
     void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
 
+    void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);
+
     ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
     void requestHintsFromListener(in INotificationListener token, int hints);
     int getHintsFromListener(in INotificationListener token);
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 56cd53e..ebb3c43 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import android.Manifest;
+import android.annotation.RequiresPermission;
 import android.app.trust.ITrustManager;
 import android.content.Context;
 import android.content.Intent;
@@ -111,6 +113,7 @@
          *
          * @see #reenableKeyguard()
          */
+        @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
         public void disableKeyguard() {
             try {
                 mWM.disableKeyguard(mToken, mTag);
@@ -132,6 +135,7 @@
          *
          * @see #disableKeyguard()
          */
+        @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
         public void reenableKeyguard() {
             try {
                 mWM.reenableKeyguard(mToken);
@@ -302,6 +306,7 @@
      *   once the user has gotten past the keyguard.
      */
     @Deprecated
+    @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
     public void exitKeyguardSecurely(final OnKeyguardExitResult callback) {
         try {
             mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a20aa668..47133d4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2651,14 +2651,12 @@
 
     /**
      * @hide
-     * Sets the given package as the device owner. The package must already be installed and there
-     * shouldn't be an existing device owner registered, for this call to succeed. Also, this
-     * method must be called before the device is provisioned.
+     * Sets the given package as the device owner.
+     * Same as {@link #setDeviceOwner(String, String)} but without setting a device owner name.
      * @param packageName the package name of the application to be registered as the device owner.
      * @return whether the package was successfully registered as the device owner.
      * @throws IllegalArgumentException if the package name is null or invalid
-     * @throws IllegalStateException if a device owner is already registered or the device has
-     *         already been provisioned.
+     * @throws IllegalStateException If the preconditions mentioned are not met.
      */
     public boolean setDeviceOwner(String packageName) throws IllegalArgumentException,
             IllegalStateException {
@@ -2667,15 +2665,17 @@
 
     /**
      * @hide
-     * Sets the given package as the device owner. The package must already be installed and there
-     * shouldn't be an existing device owner registered, for this call to succeed. Also, this
-     * method must be called before the device is provisioned.
+     * Sets the given package as the device owner. The package must already be installed. There
+     * must not already be a device owner.
+     * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
+     * this method.
+     * Calling this after the setup phase of the primary user has completed is allowed only if
+     * the caller is the shell uid, and there are no additional users and no accounts.
      * @param packageName the package name of the application to be registered as the device owner.
      * @param ownerName the human readable name of the institution that owns this device.
      * @return whether the package was successfully registered as the device owner.
      * @throws IllegalArgumentException if the package name is null or invalid
-     * @throws IllegalStateException if a device owner is already registered or the device has
-     *         already been provisioned.
+     * @throws IllegalStateException If the preconditions mentioned are not met.
      */
     public boolean setDeviceOwner(String packageName, String ownerName)
             throws IllegalArgumentException, IllegalStateException {
@@ -2961,14 +2961,18 @@
     /**
      * @hide
      * Sets the given component as the profile owner of the given user profile. The package must
-     * already be installed and there shouldn't be an existing profile owner registered for this
-     * user. Only the system can call this API if the user has already completed setup.
+     * already be installed. There must not already be a profile owner for this user.
+     * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
+     * this method.
+     * Calling this after the setup phase of the specified user has completed is allowed only if:
+     * - the caller is SYSTEM_UID.
+     * - or the caller is the shell uid, and there are no accounts on the specified user.
      * @param admin the component name to be registered as profile owner.
      * @param ownerName the human readable name of the organisation associated with this DPM.
      * @param userHandle the userId to set the profile owner for.
      * @return whether the component was successfully registered as the profile owner.
-     * @throws IllegalArgumentException if admin is null, the package isn't installed, or
-     *         the user has already been set up.
+     * @throws IllegalArgumentException if admin is null, the package isn't installed, or the
+     * preconditions mentioned are not met.
      */
     public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle)
             throws IllegalArgumentException {
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4ed1489..23659e3 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,4 +30,6 @@
     ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
             String callingPackage);
     UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
+    void setAppIdle(String packageName, boolean idle, int userId);
+    boolean isAppIdle(String packageName, int userId);
 }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index bc6099a..8a01d66 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 
 import java.util.Collections;
@@ -217,4 +218,20 @@
         }
         return aggregatedStats;
     }
+
+    /**
+     * Returns whether the specified app is currently considered idle. This will be true if the
+     * app hasn't been used directly or indirectly for a period of time defined by the system. This
+     * could be of the order of several hours or days.
+     * @param packageName The package name of the app to query
+     * @return whether the app is currently considered idle
+     */
+    public boolean isAppIdle(String packageName) {
+        try {
+            return mService.isAppIdle(packageName, UserHandle.myUserId());
+        } catch (RemoteException e) {
+            // fall through and return default
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 79e560f..3044a94 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -130,7 +130,7 @@
      * {@link #STATE_OFF},
      * {@link #STATE_TURNING_ON},
      * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
+     * {@link #STATE_TURNING_OFF}
      */
     public static final String EXTRA_PREVIOUS_STATE =
             "android.bluetooth.adapter.extra.PREVIOUS_STATE";
@@ -1301,9 +1301,12 @@
     public boolean isHardwareTrackingFiltersAvailable() {
         if (getState() != STATE_ON) return false;
         try {
-            synchronized(mManagerCallback) {
-                if(mService != null) return (mService.numOfHwTrackFiltersAvailable() != 0);
+            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
+            if (iGatt == null) {
+                // BLE is not supported
+                return false;
             }
+            return (iGatt.numHwTrackFiltersAvailable() != 0);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
diff --git a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
index cdb24f4..01778b3 100644
--- a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
+++ b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
@@ -123,4 +123,7 @@
     public void onFoundOrLost(boolean onFound, ScanResult scanResult) throws RemoteException {
     }
 
+    @Override
+    public void onScanManagerErrorCallback(int errorCode) throws RemoteException {
+    }
 }
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index f6001bf..a3eceb5 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -100,7 +100,6 @@
     boolean isActivityAndEnergyReportingSupported();
     void getActivityEnergyInfoFromController();
     BluetoothActivityEnergyInfo reportActivityInfo();
-    int numOfHwTrackFiltersAvailable();
 
     // for dumpsys support
     String dump();
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 4ca57f8..72abeaf 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -103,4 +103,5 @@
                             in boolean confirm, in byte[] value);
     void disconnectAll();
     void unregAll();
+    int numHwTrackFiltersAvailable();
 }
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index 91e62ea..cbba9f0 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -67,6 +67,7 @@
     void onReadRemoteRssi(in String address, in int rssi, in int status);
     void onMultiAdvertiseCallback(in int status, boolean isStart,
                                   in AdvertiseSettings advertiseSettings);
+    void onScanManagerErrorCallback(in int errorCode);
     void onConfigureMTU(in String address, in int mtu, in int status);
     void onFoundOrLost(in boolean onFound, in ScanResult scanResult);
 }
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 3078951..687bd5d 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -381,6 +381,18 @@
                 }
             });
         }
+
+        @Override
+        public void onScanManagerErrorCallback(final int errorCode) {
+            if (VDBG) {
+                Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode);
+            }
+            synchronized (this) {
+                if (mClientIf <= 0)
+                    return;
+            }
+            postCallbackError(mScanCallback, errorCode);
+        }
     }
 
     private void postCallbackError(final ScanCallback callback, final int errorCode) {
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 31e6e25..0cf8df1 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -65,6 +65,12 @@
 public abstract class CameraCaptureSession implements AutoCloseable {
 
     /**
+     * Used to identify invalid session ID.
+     * @hide
+     */
+    public static final int SESSION_ID_NONE = -1;
+
+    /**
      * Get the camera device that this session is created for.
      */
     public abstract CameraDevice getDevice();
@@ -168,10 +174,11 @@
      * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
      *                                  configured as outputs for this session; or a reprocess
      *                                  capture request is submitted in a non-reprocessible capture
-     *                                  session; or the capture targets a Surface in the middle
-     *                                  of being {@link #prepare prepared}; or the handler is
-     *                                  null, the listener is not null, and the calling thread has
-     *                                  no looper.
+     *                                  session; or the reprocess capture request was created with
+     *                                  a {@link TotalCaptureResult} from a different session; or
+     *                                  the capture targets a Surface in the middle of being
+     *                                  {@link #prepare prepared}; or the handler is null, the
+     *                                  listener is not null, and the calling thread has no looper.
      *
      * @see #captureBurst
      * @see #setRepeatingRequest
@@ -226,7 +233,9 @@
      *                                  capture request is submitted in a non-reprocessible capture
      *                                  session; or the list of requests contains both requests to
      *                                  capture images from the camera and reprocess capture
-     *                                  requests; or one of the captures targets a Surface in the
+     *                                  requests; or one of the reprocess capture requests was
+     *                                  created with a {@link TotalCaptureResult} from a different
+     *                                  session; or one of the captures targets a Surface in the
      *                                  middle of being {@link #prepare prepared}; or if the handler
      *                                  is null, the listener is not null, and the calling thread
      *                                  has no looper.
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 35727e8..19d17b1 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -158,6 +158,9 @@
     private final HashSet<Surface> mSurfaceSet;
     private final CameraMetadataNative mSettings;
     private boolean mIsReprocess;
+    // Each reprocess request must be tied to a reprocessible session ID.
+    // Valid only for reprocess requests (mIsReprocess == true).
+    private int mReprocessibleSessionId;
 
     private Object mUserTag;
 
@@ -170,6 +173,7 @@
         mSettings = new CameraMetadataNative();
         mSurfaceSet = new HashSet<Surface>();
         mIsReprocess = false;
+        mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
     }
 
     /**
@@ -182,6 +186,7 @@
         mSettings = new CameraMetadataNative(source.mSettings);
         mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
         mIsReprocess = source.mIsReprocess;
+        mReprocessibleSessionId = source.mReprocessibleSessionId;
         mUserTag = source.mUserTag;
     }
 
@@ -189,11 +194,36 @@
      * Take ownership of passed-in settings.
      *
      * Used by the Builder to create a mutable CaptureRequest.
+     *
+     * @param settings Settings for this capture request.
+     * @param isReprocess Indicates whether to create a reprocess capture request. {@code true}
+     *                    to create a reprocess capture request. {@code false} to create a regular
+     *                    capture request.
+     * @param reprocessibleSessionId The ID of the camera capture session this capture is created
+     *                               for. This is used to validate if the application submits a
+     *                               reprocess capture request to the same session where
+     *                               the {@link TotalCaptureResult}, used to create the reprocess
+     *                               capture, came from.
+     *
+     * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+     *                                  reprocessibleSessionId.
+     *
+     * @see CameraDevice#createReprocessCaptureRequest
      */
-    private CaptureRequest(CameraMetadataNative settings, boolean isReprocess) {
+    private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
+            int reprocessibleSessionId) {
         mSettings = CameraMetadataNative.move(settings);
         mSurfaceSet = new HashSet<Surface>();
         mIsReprocess = isReprocess;
+        if (isReprocess) {
+            if (reprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+                throw new IllegalArgumentException("Create a reprocess capture request with an " +
+                        "invalid session ID: " + reprocessibleSessionId);
+            }
+            mReprocessibleSessionId = reprocessibleSessionId;
+        } else {
+            mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
+        }
     }
 
     /**
@@ -277,6 +307,23 @@
     }
 
     /**
+     * Get the reprocessible session ID this reprocess capture request is associated with.
+     *
+     * @return the reprocessible session ID this reprocess capture request is associated with
+     *
+     * @throws IllegalStateException if this capture request is not a reprocess capture request.
+     * @hide
+     */
+    public int getReprocessibleSessionId() {
+        if (mIsReprocess == false ||
+                mReprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+            throw new IllegalStateException("Getting the reprocessible session ID for a "+
+                    "non-reprocess capture request is illegal.");
+        }
+        return mReprocessibleSessionId;
+    }
+
+    /**
      * Determine whether this CaptureRequest is equal to another CaptureRequest.
      *
      * <p>A request is considered equal to another is if it's set of key/values is equal, it's
@@ -298,7 +345,8 @@
                 && Objects.equals(mUserTag, other.mUserTag)
                 && mSurfaceSet.equals(other.mSurfaceSet)
                 && mSettings.equals(other.mSettings)
-                && mIsReprocess == other.mIsReprocess;
+                && mIsReprocess == other.mIsReprocess
+                && mReprocessibleSessionId == other.mReprocessibleSessionId;
     }
 
     @Override
@@ -347,6 +395,7 @@
         }
 
         mIsReprocess = (in.readInt() == 0) ? false : true;
+        mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
     }
 
     @Override
@@ -397,10 +446,23 @@
          * Initialize the builder using the template; the request takes
          * ownership of the template.
          *
+         * @param template Template settings for this capture request.
+         * @param reprocess Indicates whether to create a reprocess capture request. {@code true}
+         *                  to create a reprocess capture request. {@code false} to create a regular
+         *                  capture request.
+         * @param reprocessibleSessionId The ID of the camera capture session this capture is
+         *                               created for. This is used to validate if the application
+         *                               submits a reprocess capture request to the same session
+         *                               where the {@link TotalCaptureResult}, used to create the
+         *                               reprocess capture, came from.
+         *
+         * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+         *                                  reprocessibleSessionId.
          * @hide
          */
-        public Builder(CameraMetadataNative template, boolean reprocess) {
-            mRequest = new CaptureRequest(template, reprocess);
+        public Builder(CameraMetadataNative template, boolean reprocess,
+                int reprocessibleSessionId) {
+            mRequest = new CaptureRequest(template, reprocess, reprocessibleSessionId);
         }
 
         /**
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 6f7dd78..fb3c098 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -50,6 +50,7 @@
 public final class TotalCaptureResult extends CaptureResult {
 
     private final List<CaptureResult> mPartialResults;
+    private final int mSessionId;
 
     /**
      * Takes ownership of the passed-in camera metadata and the partial results
@@ -58,7 +59,7 @@
      * @hide
      */
     public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
-            CaptureResultExtras extras, List<CaptureResult> partials) {
+            CaptureResultExtras extras, List<CaptureResult> partials, int sessionId) {
         super(results, parent, extras);
 
         if (partials == null) {
@@ -66,6 +67,8 @@
         } else {
             mPartialResults = partials;
         }
+
+        mSessionId = sessionId;
     }
 
     /**
@@ -78,6 +81,7 @@
         super(results, sequenceId);
 
         mPartialResults = new ArrayList<>();
+        mSessionId = CameraCaptureSession.SESSION_ID_NONE;
     }
 
     /**
@@ -95,4 +99,14 @@
     public List<CaptureResult> getPartialResults() {
         return Collections.unmodifiableList(mPartialResults);
     }
+
+    /**
+     * Get the ID of the session where the capture request of this result was submitted.
+     *
+     * @return The session ID
+     * @hide
+     */
+    public int getSessionId() {
+        return mSessionId;
+    }
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index c74204d..3c19529 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -156,9 +156,10 @@
         } else if (request.isReprocess() && !isReprocessible()) {
             throw new IllegalArgumentException("this capture session cannot handle reprocess " +
                     "requests");
+        } else if (request.isReprocess() && request.getReprocessibleSessionId() != mId) {
+            throw new IllegalArgumentException("capture request was created for another session");
         }
 
-
         checkNotClosed();
 
         handler = checkHandler(handler, callback);
@@ -185,12 +186,17 @@
         if (reprocess && !isReprocessible()) {
             throw new IllegalArgumentException("this capture session cannot handle reprocess " +
                     "requests");
+        } else if (reprocess && requests.get(0).getReprocessibleSessionId() != mId) {
+            throw new IllegalArgumentException("capture request was created for another session");
         }
 
         for (int i = 1; i < requests.size(); i++) {
             if (requests.get(i).isReprocess() != reprocess) {
                 throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
                         " requests");
+            } else if (reprocess && requests.get(i).getReprocessibleSessionId() != mId) {
+                throw new IllegalArgumentException("capture request was created for another " +
+                    "session");
             }
         }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1e680dfd..ff4ad79 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -585,8 +585,8 @@
                 return null;
             }
 
-            CaptureRequest.Builder builder =
-                    new CaptureRequest.Builder(templatedRequest, /*reprocess*/false);
+            CaptureRequest.Builder builder = new CaptureRequest.Builder(
+                    templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
 
             return builder;
         }
@@ -601,7 +601,8 @@
             CameraMetadataNative resultMetadata = new
                     CameraMetadataNative(inputResult.getNativeCopy());
 
-            return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true);
+            return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
+                    inputResult.getSessionId());
         }
     }
 
@@ -763,7 +764,7 @@
 
             if (callback != null) {
                 mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback,
-                        requestList, handler, repeating));
+                        requestList, handler, repeating, mNextSessionId - 1));
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "Listen for request " + requestId + " is null");
@@ -1095,9 +1096,10 @@
         private final CaptureCallback mCallback;
         private final List<CaptureRequest> mRequestList;
         private final Handler mHandler;
+        private final int mSessionId;
 
         CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
-                Handler handler, boolean repeating) {
+                Handler handler, boolean repeating, int sessionId) {
             if (callback == null || handler == null) {
                 throw new UnsupportedOperationException(
                     "Must have a valid handler and a valid callback");
@@ -1106,6 +1108,7 @@
             mHandler = handler;
             mRequestList = new ArrayList<CaptureRequest>(requestList);
             mCallback = callback;
+            mSessionId = sessionId;
         }
 
         public boolean isRepeating() {
@@ -1140,6 +1143,10 @@
             return mHandler;
         }
 
+        public int getSessionId() {
+            return mSessionId;
+        }
+
     }
 
     /**
@@ -1643,8 +1650,8 @@
                     List<CaptureResult> partialResults =
                             mFrameNumberTracker.popPartialResults(frameNumber);
 
-                    final TotalCaptureResult resultAsCapture =
-                            new TotalCaptureResult(result, request, resultExtras, partialResults);
+                    final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
+                            request, resultExtras, partialResults, holder.getSessionId());
 
                     // Final capture result
                     resultDispatch = new Runnable() {
@@ -1665,7 +1672,8 @@
                 holder.getHandler().post(resultDispatch);
 
                 // Collect the partials for a total result; or mark the frame as totally completed
-                mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, isReprocess);
+                mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
+                        isReprocess);
 
                 // Fire onCaptureSequenceCompleted
                 if (!isPartialResult) {
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 691798f..a4d6be09 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -326,6 +326,9 @@
         }
 
         try {
+            startPreview(); // If preview is not running (i.e. after a JPEG capture), we need to
+                            // explicitely start and stop preview before setting preview surface.
+                            // null.
             stopPreview();
         }  catch (RuntimeException e) {
             Log.e(TAG, "Received device exception in configure call: ", e);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 481fc2f..1b57055 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1440,7 +1440,8 @@
 
     void showWindowInner(boolean showInput) {
         boolean doShowInput = false;
-        boolean wasVisible = mWindowVisible;
+        final int previousImeWindowStatus =
+                (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
         mWindowVisible = true;
         if (!mShowInputRequested) {
             if (mInputStarted) {
@@ -1485,9 +1486,12 @@
             startExtractingText(false);
         }
 
-        if (!wasVisible) {
+        final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
+        if (previousImeWindowStatus != nextImeWindowStatus) {
+            mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
+        }
+        if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
             if (DEBUG) Log.v(TAG, "showWindow: showing!");
-            mImm.setImeWindowStatus(mToken, IME_ACTIVE, mBackDisposition);
             onWindowShown();
             mWindow.show();
         }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 63f48cf..c531e7e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -98,13 +98,41 @@
     public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
 
     /**
+     * The device has connected to a network that has presented a captive
+     * portal, which is blocking Internet connectivity. The user was presented
+     * with a notification that network sign in is required,
+     * and the user invoked the notification's action indicating they
+     * desire to sign in to the network. Apps handling this action should
+     * facilitate signing in to the network. This action includes a
+     * {@link Network} typed extra called {@link #EXTRA_NETWORK} that represents
+     * the network presenting the captive portal; all communication with the
+     * captive portal must be done using this {@code Network} object.
+     * <p/>
+     * When the app handling this action believes the user has signed in to
+     * the network and the captive portal has been dismissed, the app should call
+     * {@link #reportCaptivePortalDismissed} so the system can reevaluate the network.
+     * If reevaluation finds the network no longer subject to a captive portal,
+     * the network may become the default active data network.
+     * <p/>
+     * When the app handling this action believes the user explicitly wants
+     * to ignore the captive portal and the network, the app should call
+     * {@link #ignoreNetworkWithCaptivePortal}.
+     * <p/>
+     * Note that this action includes a {@code String} extra named
+     * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} that must
+     * be passed in to {@link #reportCaptivePortalDismissed} and
+     * {@link #ignoreNetworkWithCaptivePortal}.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
+
+    /**
      * The lookup key for a {@link NetworkInfo} object. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      *
      * @deprecated Since {@link NetworkInfo} can vary based on UID, applications
      *             should always obtain network information through
-     *             {@link #getActiveNetworkInfo()} or
-     *             {@link #getAllNetworkInfo()}.
+     *             {@link #getActiveNetworkInfo()}.
      * @see #EXTRA_NETWORK_TYPE
      */
     @Deprecated
@@ -112,8 +140,6 @@
 
     /**
      * Network type which triggered a {@link #CONNECTIVITY_ACTION} broadcast.
-     * Can be used with {@link #getNetworkInfo(int)} to get {@link NetworkInfo}
-     * state based on the calling application.
      *
      * @see android.content.Intent#getIntExtra(String, int)
      */
@@ -163,6 +189,15 @@
     public static final String EXTRA_INET_CONDITION = "inetCondition";
 
     /**
+     * The lookup key for a string that is sent out with
+     * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}. This string must be
+     * passed in to {@link #reportCaptivePortalDismissed} and
+     * {@link #ignoreNetworkWithCaptivePortal}. Retrieve it with
+     * {@link android.content.Intent#getStringExtra(String)}.
+     */
+    public static final String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
+
+    /**
      * Broadcast action to indicate the change of data activity status
      * (idle or active) on a network in a recent period.
      * The network becomes active when data transmission is started, or
@@ -660,6 +695,10 @@
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
@@ -699,6 +738,10 @@
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public NetworkInfo[] getAllNetworkInfo() {
         try {
@@ -716,6 +759,9 @@
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @hide
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public Network getNetworkForType(int networkType) {
         try {
@@ -808,6 +854,10 @@
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks},
+     *             {@link #getNetworkInfo(android.net.Network)}, and
+     *             {@link #getLinkProperties(android.net.Network)} instead.
      */
     public LinkProperties getLinkProperties(int networkType) {
         try {
@@ -1748,6 +1798,82 @@
         }
     }
 
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_DISMISSED    = 0;
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED     = 1;
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate to the system that the captive portal has been
+     * dismissed.  In response the framework will re-evaluate the network's
+     * connectivity and might take further action thereafter.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     */
+    public void reportCaptivePortalDismissed(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_DISMISSED,
+                    actionToken);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate that the user does not want to pursue signing in to
+     * captive portal and the system should continue to prefer other networks
+     * without captive portals for use as the default active data network.  The
+     * system will not retest the network for a captive portal so as to avoid
+     * disturbing the user with further sign in to network notifications.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     */
+    public void ignoreNetworkWithCaptivePortal(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
+                    actionToken);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate the user wants to use this network as is, even though
+     * the captive portal is still in place.  The system will treat the network
+     * as if it did not have a captive portal when selecting the network to use
+     * as the default active data network. This may result in this network
+     * becoming the default active data network, which could disrupt network
+     * connectivity for apps because the captive portal is still in place.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @hide
+     */
+    public void useNetworkWithCaptivePortal(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS,
+                    actionToken);
+        } catch (RemoteException e) {
+        }
+    }
+
     /**
      * Set a network-independent global http proxy.  This is not normally what you want
      * for typical HTTP proxies - they are general network dependent.  However if you're
@@ -1941,6 +2067,7 @@
      * @param networkType
      *
      * {@hide}
+     * @deprecated Doesn't properly deal with multiple connected networks of the same type.
      */
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index efc76b3..77200a5 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -98,6 +98,8 @@
 
     void reportNetworkConnectivity(in Network network, boolean hasConnectivity);
 
+    void captivePortalAppResponse(in Network network, int response, String actionToken);
+
     ProxyInfo getGlobalProxy();
 
     void setGlobalProxy(in ProxyInfo p);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 43309c0..8c1f44f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -115,15 +115,15 @@
  * later reading.</p>
  * 
  * <p>There are also some methods that provide a more efficient way to work
- * with Parcelables: {@link #writeTypedArray},
- * {@link #writeTypedList(List)},
- * {@link #readTypedArray} and {@link #readTypedList}.  These methods
+ * with Parcelables: {@link #writeTypedObject}, {@link #writeTypedArray},
+ * {@link #writeTypedList}, {@link #readTypedObject},
+ * {@link #createTypedArray} and {@link #createTypedArrayList}.  These methods
  * do not write the class information of the original object: instead, the
  * caller of the read function must know what type to expect and pass in the
  * appropriate {@link Parcelable.Creator Parcelable.Creator} instead to
  * properly construct the new object and read its data.  (To more efficient
- * write and read a single Parceable object, you can directly call
- * {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
+ * write and read a single Parceable object that is not null, you can directly
+ * call {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
  * {@link Parcelable.Creator#createFromParcel Parcelable.Creator.createFromParcel}
  * yourself.)</p>
  * 
@@ -1223,6 +1223,24 @@
     }
 
     /**
+     * Flatten the Parcelable object into the parcel.
+     *
+     * @param val The Parcelable object to be written.
+     * @param parcelableFlags Contextual flags as per
+     * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+     *
+     * @see #readTypedObject
+     */
+    public final <T extends Parcelable> void writeTypedObject(T val, int parcelableFlags) {
+        if (val != null) {
+            writeInt(1);
+            val.writeToParcel(this, parcelableFlags);
+        } else {
+            writeInt(0);
+        }
+    }
+
+    /**
      * Flatten a generic object in to a parcel.  The given Object value may
      * currently be one of the following types:
      *
@@ -2138,6 +2156,25 @@
     }
 
     /**
+     * Read and return a typed Parcelable object from a parcel.
+     * Returns null if the previous written object was null.
+     * The object <em>must</em> have previous been written via
+     * {@link #writeTypedObject} with the same object type.
+     *
+     * @return A newly created object of the type that was previously
+     *         written.
+     *
+     * @see #writeTypedObject
+     */
+    public final <T> T readTypedObject(Parcelable.Creator<T> c) {
+        if (readInt() != 0) {
+            return c.createFromParcel(this);
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Write a heterogeneous array of Parcelable objects into the Parcel.
      * Each object in the array is written along with its class name, so
      * that the correct class can later be instantiated.  As a result, this
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 0b1031c..16e0bf7 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -16,6 +16,7 @@
 
 package android.os.storage;
 
+import android.content.pm.IPackageMoveObserver;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.IInterface;
@@ -1082,12 +1083,14 @@
             }
 
             @Override
-            public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException {
+            public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
+                    throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
                     _data.writeString(volumeUuid);
+                    _data.writeStrongBinder((callback != null ? callback.asBinder() : null));
                     mRemote.transact(Stub.TRANSACTION_setPrimaryStorageUuid, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -1714,7 +1717,9 @@
                 case TRANSACTION_setPrimaryStorageUuid: {
                     data.enforceInterface(DESCRIPTOR);
                     String volumeUuid = data.readString();
-                    setPrimaryStorageUuid(volumeUuid);
+                    IPackageMoveObserver listener = IPackageMoveObserver.Stub.asInterface(
+                            data.readStrongBinder());
+                    setPrimaryStorageUuid(volumeUuid, listener);
                     reply.writeNoException();
                     return true;
                 }
@@ -2020,5 +2025,6 @@
     public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException;
 
     public String getPrimaryStorageUuid() throws RemoteException;
-    public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException;
+    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
+            throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 747fb40..6116aef 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -22,6 +22,8 @@
 import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.IPackageMoveObserver;
+import android.content.pm.PackageManager;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -642,7 +644,12 @@
         }
     }
 
-    /** {@hide} */
+    /**
+     * This is not the API you're looking for.
+     *
+     * @see PackageManager#getPrimaryStorageCurrentVolume()
+     * @hide
+     */
     public String getPrimaryStorageUuid() {
         try {
             return mMountService.getPrimaryStorageUuid();
@@ -651,10 +658,15 @@
         }
     }
 
-    /** {@hide} */
-    public void setPrimaryStorageUuid(String volumeUuid) {
+    /**
+     * This is not the API you're looking for.
+     *
+     * @see PackageManager#movePrimaryStorage(VolumeInfo)
+     * @hide
+     */
+    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
         try {
-            mMountService.setPrimaryStorageUuid(volumeUuid);
+            mMountService.setPrimaryStorageUuid(volumeUuid, callback);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 69a05c4..bae06b8 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.RequiresPermission;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -32,6 +33,9 @@
 import android.util.Log;
 import android.webkit.WebIconDatabase;
 
+import static android.Manifest.permission.READ_HISTORY_BOOKMARKS;
+import static android.Manifest.permission.WRITE_HISTORY_BOOKMARKS;
+
 public class Browser {
     private static final String LOGTAG = "browser";
 
@@ -41,6 +45,8 @@
      * {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
      * requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
      */
+    @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+    @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
     public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
 
     /**
@@ -122,6 +128,8 @@
      * {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
      * requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
      */
+    @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+    @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
     public static final Uri SEARCHES_URI = Uri.parse("content://browser/searches");
 
     /**
@@ -233,6 +241,7 @@
      *
      *  @param cr   The ContentResolver used to access the database.
      */
+    @RequiresPermission(READ_HISTORY_BOOKMARKS)
     public static final Cursor getAllBookmarks(ContentResolver cr) throws
             IllegalStateException {
         return cr.query(Bookmarks.CONTENT_URI,
@@ -248,6 +257,7 @@
      *
      *  @param cr   The ContentResolver used to access the database.
      */
+    @RequiresPermission(READ_HISTORY_BOOKMARKS)
     public static final Cursor getAllVisitedUrls(ContentResolver cr) throws
             IllegalStateException {
         return cr.query(Combined.CONTENT_URI,
@@ -308,6 +318,7 @@
      *  @param real If true, this is an actual visit, and should add to the
      *              number of visits.  If false, the user entered it manually.
      */
+    @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
     public static final void updateVisitedHistory(ContentResolver cr,
                                                   String url, boolean real) {
         long now = System.currentTimeMillis();
@@ -358,6 +369,7 @@
      *  @param cr   The ContentResolver used to access the database.
      *  @hide pending API council approval
      */
+    @RequiresPermission(READ_HISTORY_BOOKMARKS)
     public static final String[] getVisitedHistory(ContentResolver cr) {
         Cursor c = null;
         String[] str = null;
@@ -393,6 +405,7 @@
      *
      * @param cr The ContentResolver used to access the database.
      */
+    @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
     public static final void truncateHistory(ContentResolver cr) {
         // TODO make a single request to the provider to do this in a single transaction
         Cursor cursor = null;
@@ -424,6 +437,7 @@
      * @param cr   The ContentResolver used to access the database.
      * @return boolean  True if the history can be cleared.
      */
+    @RequiresPermission(READ_HISTORY_BOOKMARKS)
     public static final boolean canClearHistory(ContentResolver cr) {
         Cursor cursor = null;
         boolean ret = false;
@@ -446,6 +460,7 @@
      *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
      *  @param cr   The ContentResolver used to access the database.
      */
+    @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
     public static final void clearHistory(ContentResolver cr) {
         deleteHistoryWhere(cr, null);
     }
@@ -461,6 +476,7 @@
      * @param whereClause   String to limit the items affected.
      *                      null means all items.
      */
+    @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
     private static final void deleteHistoryWhere(ContentResolver cr, String whereClause) {
         Cursor cursor = null;
         try {
@@ -486,6 +502,7 @@
      * @param end   Last date to remove. If -1, all dates after begin.
      *              Non-inclusive.
      */
+    @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
     public static final void deleteHistoryTimeFrame(ContentResolver cr,
             long begin, long end) {
         String whereClause;
@@ -511,6 +528,7 @@
      * @param cr    The ContentResolver used to access the database.
      * @param url   url to remove.
      */
+    @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
     public static final void deleteFromHistory(ContentResolver cr,
                                                String url) {
         cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url });
@@ -523,6 +541,7 @@
      * @param cr   The ContentResolver used to access the database.
      * @param search    The string to add to the searches database.
      */
+    @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
     public static final void addSearchUrl(ContentResolver cr, String search) {
         // The content provider will take care of updating existing searches instead of duplicating
         ContentValues values = new ContentValues();
@@ -536,6 +555,7 @@
      *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
      * @param cr   The ContentResolver used to access the database.
      */
+    @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
     public static final void clearSearches(ContentResolver cr) {
         // FIXME: Should this clear the urls to which these searches lead?
         // (i.e. remove google.com/query= blah blah blah)
@@ -557,6 +577,7 @@
      *  @param  listener IconListener that gets the icons once they are
      *          retrieved.
      */
+    @RequiresPermission(READ_HISTORY_BOOKMARKS)
     public static final void requestAllIcons(ContentResolver cr, String where,
             WebIconDatabase.IconListener listener) {
         // Do nothing: this is no longer used.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index cc7f880..35b8819 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -358,6 +358,20 @@
     }
 
     /**
+     * Inform the notification manager that these notifications have been viewed by the
+     * user.
+     * @param keys Notifications to mark as seen.
+     */
+    public final void setNotificationsShown(String[] keys) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().setNotificationsShownFromListener(mWrapper, keys);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
      * Sets the notification trim that will be received via {@link #onNotificationPosted}.
      *
      * <p>
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 0417921..4d1209a 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,7 +16,6 @@
 
 package android.view.animation;
 
-import android.content.res.Configuration;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -45,16 +44,6 @@
     private static final int TOGETHER = 0;
     private static final int SEQUENTIALLY = 1;
 
-    private static final float RECOMMENDED_FIELD_OF_VIEW_FOR_TV = 40f;
-    private static final float ESTIMATED_VIEWING_DISTANCE_FOR_WATCH = 11f;
-    private static final float AVERAGE_VIEWING_DISTANCE_FOR_PHONES = 14.2f;
-    private static final float N5_DIAGONAL_VIEW_ANGLE = 19.58f;
-    private static final float N5_DENSITY = 3.0f;
-    private static final float N5_DPI = 443f;
-
-    private static final float COTANGENT_OF_HALF_TV_ANGLE = (float)  (1 / Math.tan(Math.toRadians
-                (RECOMMENDED_FIELD_OF_VIEW_FOR_TV / 2)));
-
 
     /**
      * Returns the current animation time in milliseconds. This time should be used when invoking
@@ -378,78 +367,4 @@
         }
         return interpolator;
     }
-
-    /**
-     * Derives the viewing distance of a device based on the device size (in inches), and the
-     * device type.
-     * @hide
-     */
-    public static float getViewingDistance(float width, float height, int uiMode) {
-        if (uiMode == Configuration.UI_MODE_TYPE_TELEVISION) {
-            // TV
-            return (width / 2) * COTANGENT_OF_HALF_TV_ANGLE;
-        } else if (uiMode == Configuration.UI_MODE_TYPE_WATCH) {
-            // Watch
-            return ESTIMATED_VIEWING_DISTANCE_FOR_WATCH;
-        } else {
-            // Tablet, phone, etc
-            return AVERAGE_VIEWING_DISTANCE_FOR_PHONES;
-        }
-    }
-
-    /**
-     * Calculates the duration scaling factor of an animation based on the hint that the animation
-     * will move across the entire screen. A scaling factor of 1 means the duration on this given
-     * device will be the same as the duration set through
-     * {@link android.animation.Animator#setDuration(long)}. The calculation uses Nexus 5 as a
-     * baseline device. That is, the duration of the animation on a given device will scale its
-     * duration so that it has the same look and feel as the animation on Nexus 5. In order to
-     * achieve the same perceived effect of the animation across different devices, we maintain
-     * the same angular speed of the same animation in users' field of view. Therefore, the
-     * duration scale factor is determined by the ratio of the angular movement on current
-     * devices to that on the baseline device.
-     *
-     * @param width width of the screen (in inches)
-     * @param height height of the screen (in inches)
-     * @param viewingDistance the viewing distance of the device (i.e. watch, phone, TV, etc) in
-     *                        inches
-     * @return scaling factor (or multiplier) of the duration set through
-     * {@link android.animation.Animator#setDuration(long)} on current device.
-     * @hide
-     */
-    public static float getScreenSizeBasedDurationScale(float width, float height,
-            float viewingDistance) {
-        // Animation's moving distance is proportional to the screen size.
-        float diagonal = (float) Math.sqrt(width * width + height * height);
-        float diagonalViewAngle = (float) Math.toDegrees(Math.atan((diagonal / 2f)
-                / viewingDistance) * 2);
-        return diagonalViewAngle / N5_DIAGONAL_VIEW_ANGLE;
-    }
-
-    /**
-     * Calculates the duration scaling factor of an animation under the assumption that the
-     * animation is defined to move the same amount of distance (in dp) across all devices. A
-     * scaling factor of 1 means the duration on this given device will be the same as the
-     * duration set through {@link android.animation.Animator#setDuration(long)}. The calculation
-     * uses Nexus 5 as a baseline device. That is, the duration of the animation on a given
-     * device will scale its duration so that it has the same look and feel as the animation on
-     * Nexus 5. In order to achieve the same perceived effect of the animation across different
-     * devices, we maintain the same angular velocity of the same animation in users' field of
-     * view. Therefore, the duration scale factor is determined by the ratio of the angular
-     * movement on current devices to that on the baseline device.
-     *
-     * @param density logical density of the display. {@link android.util.DisplayMetrics#density}
-     * @param dpi pixels per inch
-     * @param viewingDistance viewing distance of the device (in inches)
-     * @return the scaling factor of duration
-     * @hide
-     */
-    public static float getDpBasedDurationScale(float density, float dpi,
-            float viewingDistance) {
-        // Angle in users' field of view per dp:
-        float anglePerDp = (float) Math.atan2((density / dpi) / 2, viewingDistance) * 2;
-        float baselineAnglePerDp = (float) Math.atan2((N5_DENSITY / N5_DPI) / 2,
-                AVERAGE_VIEWING_DISTANCE_FOR_PHONES) * 2;
-        return anglePerDp / baselineAnglePerDp;
-    }
 }
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index c6b4d7e..113e597 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -196,9 +196,23 @@
     }
 
     @Override
+    public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+
+        requestLayout();
+    }
+
+    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        final ImageButton leftButton = mPrevButton;
-        final ImageButton rightButton = mNextButton;
+        final ImageButton leftButton;
+        final ImageButton rightButton;
+        if (isLayoutRtl()) {
+            leftButton = mNextButton;
+            rightButton = mPrevButton;
+        } else {
+            leftButton = mPrevButton;
+            rightButton = mNextButton;
+        }
 
         final int width = right - left;
         final int height = bottom - top;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 9ecdc9c..c959774 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1854,20 +1854,19 @@
                     moved = true;
                 }
                 break;
-            case FOCUS_LEFT:
-                if (selectedPosition > startOfRowPos) {
-                    mLayoutMode = LAYOUT_MOVE_SELECTION;
-                    setSelectionInt(Math.max(0, selectedPosition - 1));
-                    moved = true;
-                }
-                break;
-            case FOCUS_RIGHT:
-                if (selectedPosition < endOfRowPos) {
-                    mLayoutMode = LAYOUT_MOVE_SELECTION;
-                    setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
-                    moved = true;
-                }
-                break;
+        }
+
+        final boolean isLayoutRtl = isLayoutRtl();
+        if (selectedPosition > startOfRowPos && ((direction == FOCUS_LEFT && !isLayoutRtl) ||
+                (direction == FOCUS_RIGHT && isLayoutRtl))) {
+            mLayoutMode = LAYOUT_MOVE_SELECTION;
+            setSelectionInt(Math.max(0, selectedPosition - 1));
+            moved = true;
+        } else if (selectedPosition < endOfRowPos && ((direction == FOCUS_LEFT && isLayoutRtl) ||
+                (direction == FOCUS_RIGHT && !isLayoutRtl))) {
+            mLayoutMode = LAYOUT_MOVE_SELECTION;
+            setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
+            moved = true;
         }
 
         if (moved) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index a50941b..10e4db3 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -1335,10 +1335,6 @@
         // If we are not connected, queue up the notifyDataSetChanged to be handled when we do
         // connect
         if (!mServiceConnection.isConnected()) {
-            if (mNotifyDataSetChangedAfterOnServiceConnected) {
-                return;
-            }
-
             mNotifyDataSetChangedAfterOnServiceConnected = true;
             requestBindService();
             return;
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 0249c22..2778f0f 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -162,7 +162,6 @@
         mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
         mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
 
-        setClickable(true);
         initPaints(res);
     }
 
@@ -318,7 +317,8 @@
         final int x = (int) (event.getX() + 0.5f);
         final int y = (int) (event.getY() + 0.5f);
 
-        switch (event.getAction()) {
+        final int action = event.getAction();
+        switch (action) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_MOVE:
                 final int touchedItem = getDayAtLocation(x, y);
@@ -326,6 +326,10 @@
                     mTouchedItem = touchedItem;
                     invalidate();
                 }
+                if (action == MotionEvent.ACTION_DOWN && touchedItem < 0) {
+                    // Touch something that's not an item, reject event.
+                    return false;
+                }
                 break;
 
             case MotionEvent.ACTION_UP:
@@ -376,9 +380,16 @@
 
         for (int col = 0; col < DAYS_IN_WEEK; col++) {
             final int colCenter = colWidth * col + colWidth / 2;
+            final int colCenterRtl;
+            if (isLayoutRtl()) {
+                colCenterRtl = mPaddedWidth - colCenter;
+            } else {
+                colCenterRtl = colCenter;
+            }
+
             final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
             final String label = getDayOfWeekLabel(dayOfWeek);
-            canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p);
+            canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p);
         }
     }
 
@@ -402,6 +413,13 @@
 
         for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) {
             final int colCenter = colWidth * col + colWidth / 2;
+            final int colCenterRtl;
+            if (isLayoutRtl()) {
+                colCenterRtl = mPaddedWidth - colCenter;
+            } else {
+                colCenterRtl = colCenter;
+            }
+
             int stateMask = 0;
 
             if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
@@ -413,12 +431,12 @@
                 stateMask |= StateSet.VIEW_STATE_ACTIVATED;
 
                 // Adjust the circle to be centered on the row.
-                canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
+                canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
             } else if (mTouchedItem == day) {
                 stateMask |= StateSet.VIEW_STATE_PRESSED;
 
                 // Adjust the circle to be centered on the row.
-                canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
+                canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
             }
 
             final boolean isDayToday = mToday == day;
@@ -431,7 +449,7 @@
             }
             p.setColor(dayTextColor);
 
-            canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p);
+            canvas.drawText(Integer.toString(day), colCenterRtl, rowCenter - halfLineHeight, p);
 
             col++;
 
@@ -583,6 +601,13 @@
     }
 
     @Override
+    public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+
+        requestLayout();
+    }
+
+    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         if (!changed) {
             return;
@@ -657,8 +682,16 @@
             return -1;
         }
 
+        // Adjust for RTL after applying padding.
+        final int paddedXRtl;
+        if (isLayoutRtl()) {
+            paddedXRtl = mPaddedWidth - paddedX;
+        } else {
+            paddedXRtl = paddedX;
+        }
+
         final int row = (paddedY - headerHeight) / mDayHeight;
-        final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+        final int col = (paddedXRtl * DAYS_IN_WEEK) / mPaddedWidth;
         final int index = col + row * DAYS_IN_WEEK;
         final int day = index + 1 - findDayOffset();
         if (day < 1 || day > mDaysInMonth) {
@@ -681,10 +714,15 @@
 
         final int index = id - 1 + findDayOffset();
 
-        // Compute left edge.
+        // Compute left edge, taking into account RTL.
         final int col = index % DAYS_IN_WEEK;
         final int colWidth = mCellWidth;
-        final int left = getPaddingLeft() + col * colWidth;
+        final int left;
+        if (isLayoutRtl()) {
+            left = getWidth() - getPaddingRight() - (col + 1) * colWidth;
+        } else {
+            left = getPaddingLeft() + col * colWidth;
+        }
 
         // Compute top edge.
         final int row = index / DAYS_IN_WEEK;
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index f7e9648..c0c8aec 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -403,7 +403,7 @@
             // No longer care about configuration changes
             mContext.unregisterReceiver(mConfigurationChangedReceiver);
 
-            mWindowManager.removeView(mContainer);
+            mWindowManager.removeViewImmediate(mContainer);
             mHandler.removeCallbacks(mPostedVisibleInitializer);
 
             if (mCallback != null) {
@@ -490,7 +490,7 @@
                     setVisible(false);
                     return true;
                 }
-                
+
             } else {
                 dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
             }
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 89990c2..191662c 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -111,7 +111,7 @@
     }
 
     private void init(Context context) {
-        ViewConfiguration vc = ViewConfiguration.get(getContext());
+        ViewConfiguration vc = ViewConfiguration.get(context);
         mSlop = vc.getScaledTouchSlop();
         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
@@ -290,7 +290,7 @@
             float deltaX = ev.getRawX() - mDownX;
             float deltaY = ev.getRawY() - mDownY;
             if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) {
-                mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2;
+                mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < Math.abs(deltaX);
             } else {
                 mSwiping = false;
             }
@@ -299,9 +299,9 @@
 
     private void updateDismiss(MotionEvent ev) {
         float deltaX = ev.getRawX() - mDownX;
+        mVelocityTracker.addMovement(ev);
+        mVelocityTracker.computeCurrentVelocity(1000);
         if (!mDismissed) {
-            mVelocityTracker.addMovement(ev);
-            mVelocityTracker.computeCurrentVelocity(1000);
 
             if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
                     ev.getRawX() >= mLastX) {
@@ -311,7 +311,9 @@
         // Check if the user tried to undo this.
         if (mDismissed && mSwiping) {
             // Check if the user's finger is actually back
-            if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO)) {
+            if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) ||
+                    // or user is flinging back left
+                    mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
                 mDismissed = false;
             }
         }
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 5c08daf..441e640 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -27,9 +27,9 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.MathUtils;
 import android.view.FocusFinder;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -43,7 +43,6 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.accessibility.AccessibilityRecord;
 import android.view.animation.Interpolator;
 import android.widget.EdgeEffect;
 import android.widget.Scroller;
@@ -84,8 +83,9 @@
  */
 public class ViewPager extends ViewGroup {
     private static final String TAG = "ViewPager";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
+    private static final int MAX_SCROLL_X = 2 << 23;
     private static final boolean USE_CACHE = false;
 
     private static final int DEFAULT_OFFSCREEN_PAGES = 1;
@@ -108,9 +108,13 @@
 
     static class ItemInfo {
         Object object;
-        int position;
         boolean scrolling;
         float widthFactor;
+
+        /** Logical position of the item within the pager adapter. */
+        int position;
+
+        /** Offset between the starting edges of the item and its container. */
         float offset;
     }
 
@@ -146,6 +150,12 @@
     private int mTopPageBounds;
     private int mBottomPageBounds;
 
+    /**
+     * The increment used to move in the "left" direction. Dependent on layout
+     * direction.
+     */
+    private int mLeftIncr = -1;
+
     // Offsets of the first and last items, if known.
     // Set during population, used to determine if we are at the beginning
     // or end of the pager data set during touch scrolling.
@@ -198,14 +208,10 @@
     // "catching" the flinging pager.
     private static final int CLOSE_ENOUGH = 2; // dp
 
-    private boolean mFakeDragging;
-    private long mFakeDragBeginTime;
-
     private final EdgeEffect mLeftEdge;
     private final EdgeEffect mRightEdge;
 
     private boolean mFirstLayout = true;
-    private boolean mNeedCalculatePageOffsets = false;
     private boolean mCalledSuper;
     private int mDecorChildCount;
 
@@ -473,7 +479,7 @@
         mAdapterChangeListener = listener;
     }
 
-    private int getClientWidth() {
+    private int getPaddedWidth() {
         return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
     }
 
@@ -504,36 +510,33 @@
         return mCurItem;
     }
 
-    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
-        setCurrentItemInternal(item, smoothScroll, always, 0);
+    boolean setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
+        return setCurrentItemInternal(item, smoothScroll, always, 0);
     }
 
-    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
+    boolean setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
         if (mAdapter == null || mAdapter.getCount() <= 0) {
             setScrollingCacheEnabled(false);
-            return;
-        }
-        if (!always && mCurItem == item && mItems.size() != 0) {
-            setScrollingCacheEnabled(false);
-            return;
+            return false;
         }
 
-        if (item < 0) {
-            item = 0;
-        } else if (item >= mAdapter.getCount()) {
-            item = mAdapter.getCount() - 1;
+        item = MathUtils.constrain(item, 0, mAdapter.getCount() - 1);
+        if (!always && mCurItem == item && mItems.size() != 0) {
+            setScrollingCacheEnabled(false);
+            return false;
         }
+
         final int pageLimit = mOffscreenPageLimit;
         if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
             // We are doing a jump by more than one page.  To avoid
             // glitches, we want to keep all current pages in the view
             // until the scroll ends.
-            for (int i=0; i<mItems.size(); i++) {
+            for (int i = 0; i < mItems.size(); i++) {
                 mItems.get(i).scrolling = true;
             }
         }
-        final boolean dispatchSelected = mCurItem != item;
 
+        final boolean dispatchSelected = mCurItem != item;
         if (mFirstLayout) {
             // We don't have any idea how big we are yet and shouldn't have any pages either.
             // Just set things up and let the pending layout handle things.
@@ -549,38 +552,55 @@
             populate(item);
             scrollToItem(item, smoothScroll, velocity, dispatchSelected);
         }
+
+        return true;
     }
 
-    private void scrollToItem(int item, boolean smoothScroll, int velocity,
+    private void scrollToItem(int position, boolean smoothScroll, int velocity,
             boolean dispatchSelected) {
-        final ItemInfo curInfo = infoForPosition(item);
-        int destX = 0;
-        if (curInfo != null) {
-            final int width = getClientWidth();
-            destX = (int) (width * Math.max(mFirstOffset,
-                    Math.min(curInfo.offset, mLastOffset)));
-        }
+        final int destX = getLeftEdgeForItem(position);
+
         if (smoothScroll) {
             smoothScrollTo(destX, 0, velocity);
+
             if (dispatchSelected && mOnPageChangeListener != null) {
-                mOnPageChangeListener.onPageSelected(item);
+                mOnPageChangeListener.onPageSelected(position);
             }
             if (dispatchSelected && mInternalPageChangeListener != null) {
-                mInternalPageChangeListener.onPageSelected(item);
+                mInternalPageChangeListener.onPageSelected(position);
             }
         } else {
             if (dispatchSelected && mOnPageChangeListener != null) {
-                mOnPageChangeListener.onPageSelected(item);
+                mOnPageChangeListener.onPageSelected(position);
             }
             if (dispatchSelected && mInternalPageChangeListener != null) {
-                mInternalPageChangeListener.onPageSelected(item);
+                mInternalPageChangeListener.onPageSelected(position);
             }
+
             completeScroll(false);
             scrollTo(destX, 0);
             pageScrolled(destX);
         }
     }
 
+    private int getLeftEdgeForItem(int position) {
+        final ItemInfo info = infoForPosition(position);
+        if (info == null) {
+            return 0;
+        }
+
+        final int width = getPaddedWidth();
+        final int scaledOffset = (int) (width * MathUtils.constrain(
+                info.offset, mFirstOffset, mLastOffset));
+
+        if (isLayoutRtl()) {
+            final int itemWidth = (int) (width * info.widthFactor + 0.5f);
+            return MAX_SCROLL_X - itemWidth - scaledOffset;
+        } else {
+            return scaledOffset;
+        }
+    }
+
     /**
      * Set a listener that will be invoked whenever the page changes or is incrementally
      * scrolled. See {@link OnPageChangeListener}.
@@ -784,7 +804,7 @@
         setScrollingCacheEnabled(true);
         setScrollState(SCROLL_STATE_SETTLING);
 
-        final int width = getClientWidth();
+        final int width = getPaddedWidth();
         final int halfWidth = width / 2;
         final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
         final float distance = halfWidth + halfWidth *
@@ -968,7 +988,7 @@
             float extraWidthLeft = 0.f;
             int itemIndex = curIndex - 1;
             ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
-            final int clientWidth = getClientWidth();
+            final int clientWidth = getPaddedWidth();
             final float leftWidthNeeded = clientWidth <= 0 ? 0 :
                     2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
             for (int pos = mCurItem - 1; pos >= 0; pos--) {
@@ -981,7 +1001,7 @@
                         mAdapter.destroyItem(this, pos, ii.object);
                         if (DEBUG) {
                             Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
-                                    " view: " + ((View) ii.object));
+                                    " view: " + ii.object);
                         }
                         itemIndex--;
                         curIndex--;
@@ -1015,7 +1035,7 @@
                             mAdapter.destroyItem(this, pos, ii.object);
                             if (DEBUG) {
                                 Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
-                                        " view: " + ((View) ii.object));
+                                        " view: " + ii.object);
                             }
                             ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                         }
@@ -1099,49 +1119,51 @@
 
     private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
         final int N = mAdapter.getCount();
-        final int width = getClientWidth();
+        final int width = getPaddedWidth();
         final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+
         // Fix up offsets for later layout.
         if (oldCurInfo != null) {
             final int oldCurPosition = oldCurInfo.position;
+
             // Base offsets off of oldCurInfo.
             if (oldCurPosition < curItem.position) {
                 int itemIndex = 0;
-                ItemInfo ii = null;
                 float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;
-                for (int pos = oldCurPosition + 1;
-                        pos <= curItem.position && itemIndex < mItems.size(); pos++) {
-                    ii = mItems.get(itemIndex);
+                for (int pos = oldCurPosition + 1; pos <= curItem.position && itemIndex < mItems.size(); pos++) {
+                    ItemInfo ii = mItems.get(itemIndex);
                     while (pos > ii.position && itemIndex < mItems.size() - 1) {
                         itemIndex++;
                         ii = mItems.get(itemIndex);
                     }
+
                     while (pos < ii.position) {
                         // We don't have an item populated for this,
                         // ask the adapter for an offset.
                         offset += mAdapter.getPageWidth(pos) + marginOffset;
                         pos++;
                     }
+
                     ii.offset = offset;
                     offset += ii.widthFactor + marginOffset;
                 }
             } else if (oldCurPosition > curItem.position) {
                 int itemIndex = mItems.size() - 1;
-                ItemInfo ii = null;
                 float offset = oldCurInfo.offset;
-                for (int pos = oldCurPosition - 1;
-                        pos >= curItem.position && itemIndex >= 0; pos--) {
-                    ii = mItems.get(itemIndex);
+                for (int pos = oldCurPosition - 1; pos >= curItem.position && itemIndex >= 0; pos--) {
+                    ItemInfo ii = mItems.get(itemIndex);
                     while (pos < ii.position && itemIndex > 0) {
                         itemIndex--;
                         ii = mItems.get(itemIndex);
                     }
+
                     while (pos > ii.position) {
                         // We don't have an item populated for this,
                         // ask the adapter for an offset.
                         offset -= mAdapter.getPageWidth(pos) + marginOffset;
                         pos--;
                     }
+
                     offset -= ii.widthFactor + marginOffset;
                     ii.offset = offset;
                 }
@@ -1155,6 +1177,7 @@
         mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
         mLastOffset = curItem.position == N - 1 ?
                 curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;
+
         // Previous pages
         for (int i = curIndex - 1; i >= 0; i--, pos--) {
             final ItemInfo ii = mItems.get(i);
@@ -1165,8 +1188,10 @@
             ii.offset = offset;
             if (ii.position == 0) mFirstOffset = offset;
         }
+
         offset = curItem.offset + curItem.widthFactor + marginOffset;
         pos = curItem.position + 1;
+
         // Next pages
         for (int i = curIndex + 1; i < itemCount; i++, pos++) {
             final ItemInfo ii = mItems.get(i);
@@ -1179,8 +1204,6 @@
             ii.offset = offset;
             offset += ii.widthFactor + marginOffset;
         }
-
-        mNeedCalculatePageOffsets = false;
     }
 
     /**
@@ -1546,34 +1569,47 @@
         // Page views. Do this once we have the right padding offsets from above.
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                ItemInfo ii;
-                if (!lp.isDecor && (ii = infoForChild(child)) != null) {
-                    int loff = (int) (childWidth * ii.offset);
-                    int childLeft = paddingLeft + loff;
-                    int childTop = paddingTop;
-                    if (lp.needsMeasure) {
-                        // This was added during layout and needs measurement.
-                        // Do it now that we know what we're working with.
-                        lp.needsMeasure = false;
-                        final int widthSpec = MeasureSpec.makeMeasureSpec(
-                                (int) (childWidth * lp.widthFactor),
-                                MeasureSpec.EXACTLY);
-                        final int heightSpec = MeasureSpec.makeMeasureSpec(
-                                (int) (height - paddingTop - paddingBottom),
-                                MeasureSpec.EXACTLY);
-                        child.measure(widthSpec, heightSpec);
-                    }
-                    if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
-                            + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
-                            + "x" + child.getMeasuredHeight());
-                    child.layout(childLeft, childTop,
-                            childLeft + child.getMeasuredWidth(),
-                            childTop + child.getMeasuredHeight());
-                }
+            if (child.getVisibility() == GONE) {
+                continue;
             }
+
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (lp.isDecor) {
+                continue;
+            }
+
+            final ItemInfo ii = infoForChild(child);
+            if (ii == null) {
+                continue;
+            }
+
+            if (lp.needsMeasure) {
+                // This was added during layout and needs measurement.
+                // Do it now that we know what we're working with.
+                lp.needsMeasure = false;
+                final int widthSpec = MeasureSpec.makeMeasureSpec(
+                        (int) (childWidth * lp.widthFactor),
+                        MeasureSpec.EXACTLY);
+                final int heightSpec = MeasureSpec.makeMeasureSpec(
+                        (int) (height - paddingTop - paddingBottom),
+                        MeasureSpec.EXACTLY);
+                child.measure(widthSpec, heightSpec);
+            }
+
+            final int childMeasuredWidth = child.getMeasuredWidth();
+            final int startOffset = (int) (childWidth * ii.offset);
+            final int childLeft;
+            if (isLayoutRtl()) {
+                childLeft = MAX_SCROLL_X - paddingRight - startOffset - childMeasuredWidth;
+            } else {
+                childLeft = paddingLeft + startOffset;
+            }
+
+            final int childTop = paddingTop;
+            child.layout(childLeft, childTop, childLeft + childMeasuredWidth,
+                    childTop + child.getMeasuredHeight());
         }
+
         mTopPageBounds = paddingTop;
         mBottomPageBounds = height - paddingBottom;
         mDecorChildCount = decorCount;
@@ -1587,13 +1623,14 @@
     @Override
     public void computeScroll() {
         if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
-            int oldX = getScrollX();
-            int oldY = getScrollY();
-            int x = mScroller.getCurrX();
-            int y = mScroller.getCurrY();
+            final int oldX = getScrollX();
+            final int oldY = getScrollY();
+            final int x = mScroller.getCurrX();
+            final int y = mScroller.getCurrY();
 
             if (oldX != x || oldY != y) {
                 scrollTo(x, y);
+
                 if (!pageScrolled(x)) {
                     mScroller.abortAnimation();
                     scrollTo(0, y);
@@ -1609,7 +1646,7 @@
         completeScroll(true);
     }
 
-    private boolean pageScrolled(int xpos) {
+    private boolean pageScrolled(int scrollX) {
         if (mItems.size() == 0) {
             mCalledSuper = false;
             onPageScrolled(0, 0, 0);
@@ -1619,12 +1656,21 @@
             }
             return false;
         }
-        final ItemInfo ii = infoForCurrentScrollPosition();
-        final int width = getClientWidth();
+
+        // Translate to scrollX to scrollStart for RTL.
+        final int scrollStart;
+        if (isLayoutRtl()) {
+            scrollStart = MAX_SCROLL_X - scrollX;
+        } else {
+            scrollStart = scrollX;
+        }
+
+        final ItemInfo ii = infoForFirstVisiblePage();
+        final int width = getPaddedWidth();
         final int widthWithMargin = width + mPageMargin;
         final float marginOffset = (float) mPageMargin / width;
         final int currentPage = ii.position;
-        final float pageOffset = (((float) xpos / width) - ii.offset) /
+        final float pageOffset = (((float) scrollStart / width) - ii.offset) /
                 (ii.widthFactor + marginOffset);
         final int offsetPixels = (int) (pageOffset * widthWithMargin);
 
@@ -1706,7 +1752,7 @@
 
                 if (lp.isDecor) continue;
 
-                final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
+                final float transformPos = (float) (child.getLeft() - scrollX) / getPaddedWidth();
                 mPageTransformer.transformPage(child, transformPos);
             }
         }
@@ -1785,11 +1831,11 @@
         // are dragging.
         if (action != MotionEvent.ACTION_DOWN) {
             if (mIsBeingDragged) {
-                if (DEBUG) Log.v(TAG, "Intercept returning true!");
+                if (DEBUG) Log.v(TAG, "Being dragged, intercept returning true!");
                 return true;
             }
             if (mIsUnableToDrag) {
-                if (DEBUG) Log.v(TAG, "Intercept returning false!");
+                if (DEBUG) Log.v(TAG, "Unable to drag, intercept returning false!");
                 return false;
             }
         }
@@ -1903,13 +1949,6 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mFakeDragging) {
-            // A fake drag is in progress already, ignore this real one
-            // but still eat the touch events.
-            // (It is likely that the user is multi-touching the screen.)
-            return true;
-        }
-
         if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
             // Don't handle edge touches immediately -- they may actually belong to one of our
             // descendants.
@@ -1978,19 +2017,26 @@
                 if (mIsBeingDragged) {
                     final VelocityTracker velocityTracker = mVelocityTracker;
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                    int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+                    final int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+
                     mPopulatePending = true;
-                    final int width = getClientWidth();
-                    final int scrollX = getScrollX();
-                    final ItemInfo ii = infoForCurrentScrollPosition();
+
+                    final float scrollStart = getScrollStart();
+                    final float scrolledPages = scrollStart / getPaddedWidth();
+                    final ItemInfo ii = infoForFirstVisiblePage();
                     final int currentPage = ii.position;
-                    final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
-                    final int activePointerIndex =
-                            ev.findPointerIndex(mActivePointerId);
+                    final float nextPageOffset;
+                    if (isLayoutRtl()) {
+                        nextPageOffset = (ii.offset - scrolledPages) / ii.widthFactor;
+                    }  else {
+                        nextPageOffset = (scrolledPages - ii.offset) / ii.widthFactor;
+                    }
+
+                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                     final float x = ev.getX(activePointerIndex);
                     final int totalDelta = (int) (x - mInitialMotionX);
-                    int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
-                            totalDelta);
+                    final int nextPage = determineTargetPage(
+                            currentPage, nextPageOffset, initialVelocity, totalDelta);
                     setCurrentItemInternal(nextPage, true, true, initialVelocity);
 
                     mActivePointerId = INVALID_POINTER;
@@ -2038,48 +2084,79 @@
     private boolean performDrag(float x) {
         boolean needsInvalidate = false;
 
+        final int width = getPaddedWidth();
         final float deltaX = mLastMotionX - x;
         mLastMotionX = x;
 
-        float oldScrollX = getScrollX();
-        float scrollX = oldScrollX + deltaX;
-        final int width = getClientWidth();
-
-        float leftBound = width * mFirstOffset;
-        float rightBound = width * mLastOffset;
-        boolean leftAbsolute = true;
-        boolean rightAbsolute = true;
-
-        final ItemInfo firstItem = mItems.get(0);
-        final ItemInfo lastItem = mItems.get(mItems.size() - 1);
-        if (firstItem.position != 0) {
-            leftAbsolute = false;
-            leftBound = firstItem.offset * width;
-        }
-        if (lastItem.position != mAdapter.getCount() - 1) {
-            rightAbsolute = false;
-            rightBound = lastItem.offset * width;
+        final EdgeEffect startEdge;
+        final EdgeEffect endEdge;
+        if (isLayoutRtl()) {
+            startEdge = mRightEdge;
+            endEdge = mLeftEdge;
+        } else {
+            startEdge = mLeftEdge;
+            endEdge = mRightEdge;
         }
 
-        if (scrollX < leftBound) {
-            if (leftAbsolute) {
-                float over = leftBound - scrollX;
-                mLeftEdge.onPull(Math.abs(over) / width);
+        // Translate scroll to relative coordinates.
+        final float nextScrollX = getScrollX() + deltaX;
+        final float scrollStart;
+        if (isLayoutRtl()) {
+            scrollStart = MAX_SCROLL_X - nextScrollX;
+        } else {
+            scrollStart = nextScrollX;
+        }
+
+        final float startBound;
+        final ItemInfo startItem = mItems.get(0);
+        final boolean startAbsolute = startItem.position == 0;
+        if (startAbsolute) {
+            startBound = startItem.offset * width;
+        } else {
+            startBound = width * mFirstOffset;
+        }
+
+        final float endBound;
+        final ItemInfo endItem = mItems.get(mItems.size() - 1);
+        final boolean endAbsolute = endItem.position == mAdapter.getCount() - 1;
+        if (endAbsolute) {
+            endBound = endItem.offset * width;
+        } else {
+            endBound = width * mLastOffset;
+        }
+
+        final float clampedScrollStart;
+        if (scrollStart < startBound) {
+            if (startAbsolute) {
+                final float over = startBound - scrollStart;
+                startEdge.onPull(Math.abs(over) / width);
                 needsInvalidate = true;
             }
-            scrollX = leftBound;
-        } else if (scrollX > rightBound) {
-            if (rightAbsolute) {
-                float over = scrollX - rightBound;
-                mRightEdge.onPull(Math.abs(over) / width);
+            clampedScrollStart = startBound;
+        } else if (scrollStart > endBound) {
+            if (endAbsolute) {
+                final float over = scrollStart - endBound;
+                endEdge.onPull(Math.abs(over) / width);
                 needsInvalidate = true;
             }
-            scrollX = rightBound;
+            clampedScrollStart = endBound;
+        } else {
+            clampedScrollStart = scrollStart;
         }
-        // Don't lose the rounded component
-        mLastMotionX += scrollX - (int) scrollX;
-        scrollTo((int) scrollX, getScrollY());
-        pageScrolled((int) scrollX);
+
+        // Translate back to absolute coordinates.
+        final float targetScrollX;
+        if (isLayoutRtl()) {
+            targetScrollX = MAX_SCROLL_X - clampedScrollStart;
+        } else {
+            targetScrollX = clampedScrollStart;
+        }
+
+        // Don't lose the rounded component.
+        mLastMotionX += targetScrollX - (int) targetScrollX;
+
+        scrollTo((int) targetScrollX, getScrollY());
+        pageScrolled((int) targetScrollX);
 
         return needsInvalidate;
     }
@@ -2088,19 +2165,23 @@
      * @return Info about the page at the current scroll position.
      *         This can be synthetic for a missing middle page; the 'object' field can be null.
      */
-    private ItemInfo infoForCurrentScrollPosition() {
-        final int width = getClientWidth();
-        final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;
+    private ItemInfo infoForFirstVisiblePage() {
+        final int startOffset = getScrollStart();
+        final int width = getPaddedWidth();
+        final float scrollOffset = width > 0 ? (float) startOffset / width : 0;
         final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+
         int lastPos = -1;
         float lastOffset = 0.f;
         float lastWidth = 0.f;
         boolean first = true;
-
         ItemInfo lastItem = null;
-        for (int i = 0; i < mItems.size(); i++) {
+
+        final int N = mItems.size();
+        for (int i = 0; i < N; i++) {
             ItemInfo ii = mItems.get(i);
-            float offset;
+
+            // Seek to position.
             if (!first && ii.position != lastPos + 1) {
                 // Create a synthetic item for a missing page.
                 ii = mTempItem;
@@ -2109,17 +2190,18 @@
                 ii.widthFactor = mAdapter.getPageWidth(ii.position);
                 i--;
             }
-            offset = ii.offset;
 
-            final float leftBound = offset;
-            final float rightBound = offset + ii.widthFactor + marginOffset;
-            if (first || scrollOffset >= leftBound) {
-                if (scrollOffset < rightBound || i == mItems.size() - 1) {
+            final float offset = ii.offset;
+            final float startBound = offset;
+            if (first || scrollOffset >= startBound) {
+                final float endBound = offset + ii.widthFactor + marginOffset;
+                if (scrollOffset < endBound || i == mItems.size() - 1) {
                     return ii;
                 }
             } else {
                 return lastItem;
             }
+
             first = false;
             lastPos = ii.position;
             lastOffset = offset;
@@ -2130,13 +2212,28 @@
         return lastItem;
     }
 
+    private int getScrollStart() {
+        if (isLayoutRtl()) {
+            return MAX_SCROLL_X - getScrollX();
+        } else {
+            return getScrollX();
+        }
+    }
+
+    /**
+     * @param currentPage the position of the page with the first visible starting edge
+     * @param pageOffset the fraction of the right-hand page that's visible
+     * @param velocity the velocity of the touch event stream
+     * @param deltaX the distance of the touch event stream
+     * @return the position of the target page
+     */
     private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
         int targetPage;
         if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
-            targetPage = velocity > 0 ? currentPage : currentPage + 1;
+            targetPage = currentPage - (velocity < 0 ? mLeftIncr : 0);
         } else {
             final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
-            targetPage = (int) (currentPage + pageOffset + truncator);
+            targetPage = (int) (currentPage - mLeftIncr * (pageOffset + truncator));
         }
 
         if (mItems.size() > 0) {
@@ -2144,7 +2241,7 @@
             final ItemInfo lastItem = mItems.get(mItems.size() - 1);
 
             // Only let the user target pages we have items for
-            targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
+            targetPage = MathUtils.constrain(targetPage, firstItem.position, lastItem.position);
         }
 
         return targetPage;
@@ -2205,6 +2302,7 @@
             int itemIndex = 0;
             ItemInfo ii = mItems.get(0);
             float offset = ii.offset;
+
             final int itemCount = mItems.size();
             final int firstPos = ii.position;
             final int lastPos = mItems.get(itemCount - 1).position;
@@ -2213,156 +2311,39 @@
                     ii = mItems.get(++itemIndex);
                 }
 
-                float drawAt;
+                final float itemOffset;
+                final float widthFactor;
                 if (pos == ii.position) {
-                    drawAt = (ii.offset + ii.widthFactor) * width;
-                    offset = ii.offset + ii.widthFactor + marginOffset;
+                    itemOffset = ii.offset;
+                    widthFactor = ii.widthFactor;
                 } else {
-                    float widthFactor = mAdapter.getPageWidth(pos);
-                    drawAt = (offset + widthFactor) * width;
-                    offset += widthFactor + marginOffset;
+                    itemOffset = offset;
+                    widthFactor = mAdapter.getPageWidth(pos);
                 }
 
-                if (drawAt + mPageMargin > scrollX) {
-                    mMarginDrawable.setBounds((int) drawAt, mTopPageBounds,
-                            (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds);
+                final float left;
+                final float scaledOffset = itemOffset * width;
+                if (isLayoutRtl()) {
+                    left = MAX_SCROLL_X - scaledOffset;
+                } else {
+                    left = scaledOffset + widthFactor * width;
+                }
+
+                offset = itemOffset + widthFactor + marginOffset;
+
+                if (left + mPageMargin > scrollX) {
+                    mMarginDrawable.setBounds((int) left, mTopPageBounds,
+                            (int) (left + mPageMargin + 0.5f), mBottomPageBounds);
                     mMarginDrawable.draw(canvas);
                 }
 
-                if (drawAt > scrollX + width) {
+                if (left > scrollX + width) {
                     break; // No more visible, no sense in continuing
                 }
             }
         }
     }
 
-    /**
-     * Start a fake drag of the pager.
-     *
-     * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager
-     * with the touch scrolling of another view, while still letting the ViewPager
-     * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
-     * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
-     * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
-     *
-     * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag
-     * is already in progress, this method will return false.
-     *
-     * @return true if the fake drag began successfully, false if it could not be started.
-     *
-     * @see #fakeDragBy(float)
-     * @see #endFakeDrag()
-     */
-    public boolean beginFakeDrag() {
-        if (mIsBeingDragged) {
-            return false;
-        }
-        mFakeDragging = true;
-        setScrollState(SCROLL_STATE_DRAGGING);
-        mInitialMotionX = mLastMotionX = 0;
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        } else {
-            mVelocityTracker.clear();
-        }
-        final long time = SystemClock.uptimeMillis();
-        final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
-        mVelocityTracker.addMovement(ev);
-        ev.recycle();
-        mFakeDragBeginTime = time;
-        return true;
-    }
-
-    /**
-     * End a fake drag of the pager.
-     *
-     * @see #beginFakeDrag()
-     * @see #fakeDragBy(float)
-     */
-    public void endFakeDrag() {
-        if (!mFakeDragging) {
-            throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
-        }
-
-        final VelocityTracker velocityTracker = mVelocityTracker;
-        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-        int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
-        mPopulatePending = true;
-        final int width = getClientWidth();
-        final int scrollX = getScrollX();
-        final ItemInfo ii = infoForCurrentScrollPosition();
-        final int currentPage = ii.position;
-        final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
-        final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
-        int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
-                totalDelta);
-        setCurrentItemInternal(nextPage, true, true, initialVelocity);
-        endDrag();
-
-        mFakeDragging = false;
-    }
-
-    /**
-     * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
-     *
-     * @param xOffset Offset in pixels to drag by.
-     * @see #beginFakeDrag()
-     * @see #endFakeDrag()
-     */
-    public void fakeDragBy(float xOffset) {
-        if (!mFakeDragging) {
-            throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
-        }
-
-        mLastMotionX += xOffset;
-
-        float oldScrollX = getScrollX();
-        float scrollX = oldScrollX - xOffset;
-        final int width = getClientWidth();
-
-        float leftBound = width * mFirstOffset;
-        float rightBound = width * mLastOffset;
-
-        final ItemInfo firstItem = mItems.get(0);
-        final ItemInfo lastItem = mItems.get(mItems.size() - 1);
-        if (firstItem.position != 0) {
-            leftBound = firstItem.offset * width;
-        }
-        if (lastItem.position != mAdapter.getCount() - 1) {
-            rightBound = lastItem.offset * width;
-        }
-
-        if (scrollX < leftBound) {
-            scrollX = leftBound;
-        } else if (scrollX > rightBound) {
-            scrollX = rightBound;
-        }
-        // Don't lose the rounded component
-        mLastMotionX += scrollX - (int) scrollX;
-        scrollTo((int) scrollX, getScrollY());
-        pageScrolled((int) scrollX);
-
-        // Synthesize an event for the VelocityTracker.
-        final long time = SystemClock.uptimeMillis();
-        final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
-                mLastMotionX, 0, 0);
-        mVelocityTracker.addMovement(ev);
-        ev.recycle();
-    }
-
-    /**
-     * Returns true if a fake drag is in progress.
-     *
-     * @return true if currently in a fake drag, false otherwise.
-     *
-     * @see #beginFakeDrag()
-     * @see #fakeDragBy(float)
-     * @see #endFakeDrag()
-     */
-    public boolean isFakeDragging() {
-        return mFakeDragging;
-    }
-
     private void onSecondaryPointerUp(MotionEvent ev) {
         final int pointerIndex = ev.getActionIndex();
         final int pointerId = ev.getPointerId(pointerIndex);
@@ -2408,7 +2389,7 @@
             return false;
         }
 
-        final int width = getClientWidth();
+        final int width = getPaddedWidth();
         final int scrollX = getScrollX();
         if (direction < 0) {
             return (scrollX > (int) (width * mFirstOffset));
@@ -2438,12 +2419,11 @@
             final int count = group.getChildCount();
             // Count backwards - let topmost views consume scroll distance first.
             for (int i = count - 1; i >= 0; i--) {
-                // TODO: Add versioned support here for transformed views.
-                // This will not work for transformed views in Honeycomb+
+                // TODO: Add support for transformed views.
                 final View child = group.getChildAt(i);
-                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
-                        y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
-                        canScroll(child, true, dx, x + scrollX - child.getLeft(),
+                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
+                        && y + scrollY >= child.getTop() && y + scrollY < child.getBottom()
+                        && canScroll(child, true, dx, x + scrollX - child.getLeft(),
                                 y + scrollY - child.getTop())) {
                     return true;
                 }
@@ -2582,19 +2562,22 @@
     }
 
     boolean pageLeft() {
-        if (mCurItem > 0) {
-            setCurrentItem(mCurItem-1, true);
-            return true;
-        }
-        return false;
+        return setCurrentItemInternal(mCurItem + mLeftIncr, true, false);
     }
 
     boolean pageRight() {
-        if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) {
-            setCurrentItem(mCurItem+1, true);
-            return true;
+        return setCurrentItemInternal(mCurItem - mLeftIncr, true, false);
+    }
+
+    @Override
+    public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+
+        if (layoutDirection == LAYOUT_DIRECTION_LTR) {
+            mLeftIncr = -1;
+        } else {
+            mLeftIncr = 1;
         }
-        return false;
     }
 
     /**
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 8b2c269..2f6a69c 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -189,32 +189,44 @@
     return getAudioTrack(env, audioTrackObj);
 }
 
+// This function converts Java channel masks to a native channel mask.
+// validity should be checked with audio_is_output_channel().
+static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
+        jint channelPositionMask, jint channelIndexMask)
+{
+    if (channelIndexMask != 0) {  // channel index mask takes priority
+        // To convert to a native channel mask, the Java channel index mask
+        // requires adding the index representation.
+        return audio_channel_mask_from_representation_and_bits(
+                        AUDIO_CHANNEL_REPRESENTATION_INDEX,
+                        channelIndexMask);
+    }
+    // To convert to a native channel mask, the Java channel position mask
+    // requires a shift by 2 to skip the two deprecated channel
+    // configurations "default" and "mono".
+    return (audio_channel_mask_t)(channelPositionMask >> 2);
+}
+
 // ----------------------------------------------------------------------------
 static jint
 android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
         jobject jaa,
-        jint sampleRateInHertz, jint javaChannelMask,
+        jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,
         jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
 
-    ALOGV("sampleRate=%d, audioFormat(from Java)=%d, channel mask=%x, buffSize=%d",
-        sampleRateInHertz, audioFormat, javaChannelMask, buffSizeInBytes);
+    ALOGV("sampleRate=%d, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d",
+        sampleRateInHertz, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes);
 
     if (jaa == 0) {
         ALOGE("Error creating AudioTrack: invalid audio attributes");
         return (jint) AUDIO_JAVA_ERROR;
     }
 
-    // Java channel masks don't map directly to the native definition for positional
-    // channel masks: it's a shift by 2 to skip the two deprecated channel
-    // configurations "default" and "mono".
     // Invalid channel representations are caught by !audio_is_output_channel() below.
-    audio_channel_mask_t nativeChannelMask =
-            audio_channel_mask_get_representation(javaChannelMask)
-                == AUDIO_CHANNEL_REPRESENTATION_POSITION
-            ? javaChannelMask >> 2 : javaChannelMask;
-
+    audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
+            channelPositionMask, channelIndexMask);
     if (!audio_is_output_channel(nativeChannelMask)) {
-        ALOGE("Error creating AudioTrack: invalid channel mask %#x.", javaChannelMask);
+        ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
         return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
     }
 
@@ -982,7 +994,7 @@
     {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
     {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
     {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
-    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;IIIII[I)I",
+    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;IIIIII[I)I",
                                          (void *)android_media_AudioTrack_setup},
     {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 62685a1..018c1a1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -212,6 +212,7 @@
     <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" />
     <protected-broadcast android:name="android.intent.action.MEDIA_EJECT" />
 
+    <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
     <!-- @deprecated.  Only {@link android.net.ConnectivityManager.CONNECTIVITY_ACTION} is sent. -->
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
@@ -1309,6 +1310,14 @@
     <permission android:name="android.permission.MANAGE_USERS"
         android:protectionLevel="signature|system" />
 
+    <!-- @hide Allows an application to set the profile owners and the device owner.
+         This permission is not available to third party applications.-->
+    <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_manageProfileAndDeviceOwners"
+        android:description="@string/permdesc_manageProfileAndDeviceOwners" />
+
     <!-- Allows an application to get full detailed information about
          recently running tasks, with full fidelity to the real state.
          @hide -->
@@ -2097,6 +2106,11 @@
         android:protectionLevel="signature|development|appop" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
+    <!-- @hide Allows an application to change the app idle state of an app.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to collect battery statistics -->
     <permission android:name="android.permission.BATTERY_STATS"
         android:protectionLevel="signature|system|development" />
diff --git a/core/res/res/drawable/ic_chevron_right.xml b/core/res/res/drawable/ic_chevron_end.xml
similarity index 91%
rename from core/res/res/drawable/ic_chevron_right.xml
rename to core/res/res/drawable/ic_chevron_end.xml
index 4e6d8e3..8570d26 100644
--- a/core/res/res/drawable/ic_chevron_right.xml
+++ b/core/res/res/drawable/ic_chevron_end.xml
@@ -18,7 +18,8 @@
         android:height="24dp"
         android:viewportWidth="24"
         android:viewportHeight="24"
-        android:tint="?attr/colorControlNormal">
+        android:tint="?attr/colorControlNormal"
+        android:autoMirrored="true">
     <path
         android:fillColor="#FF000000"
         android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6,-6z"/>
diff --git a/core/res/res/drawable/ic_chevron_left.xml b/core/res/res/drawable/ic_chevron_start.xml
similarity index 91%
rename from core/res/res/drawable/ic_chevron_left.xml
rename to core/res/res/drawable/ic_chevron_start.xml
index dc24706..d412ce0 100644
--- a/core/res/res/drawable/ic_chevron_left.xml
+++ b/core/res/res/drawable/ic_chevron_start.xml
@@ -18,7 +18,8 @@
         android:height="24dp"
         android:viewportWidth="24"
         android:viewportHeight="24"
-        android:tint="?attr/colorControlNormal">
+        android:tint="?attr/colorControlNormal"
+        android:autoMirrored="true">
     <path
         android:fillColor="#FF000000"
         android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41,-1.41L10.83 12z"/>
diff --git a/core/res/res/drawable/list_highlight_material.xml b/core/res/res/drawable/list_choice_background_material.xml
similarity index 100%
rename from core/res/res/drawable/list_highlight_material.xml
rename to core/res/res/drawable/list_choice_background_material.xml
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index 8125544..2150341 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -42,7 +42,7 @@
 
     <TextView
         android:id="@+id/date_picker_header_date"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
         android:gravity="start"
diff --git a/core/res/res/layout/day_picker_content_material.xml b/core/res/res/layout/day_picker_content_material.xml
index 1852bfa..b582d74 100644
--- a/core/res/res/layout/day_picker_content_material.xml
+++ b/core/res/res/layout/day_picker_content_material.xml
@@ -30,7 +30,7 @@
         android:layout_height="wrap_content"
         android:minWidth="48dp"
         android:minHeight="48dp"
-        android:src="@drawable/ic_chevron_left"
+        android:src="@drawable/ic_chevron_start"
         android:background="?attr/selectableItemBackgroundBorderless"
         android:contentDescription="@string/date_picker_prev_month_button"
         android:visibility="invisible" />
@@ -41,7 +41,7 @@
         android:layout_height="wrap_content"
         android:minWidth="48dp"
         android:minHeight="48dp"
-        android:src="@drawable/ic_chevron_right"
+        android:src="@drawable/ic_chevron_end"
         android:background="?attr/selectableItemBackgroundBorderless"
         android:contentDescription="@string/date_picker_next_month_button"
         android:visibility="invisible" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 00c771d8..887b0a5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6038,19 +6038,8 @@
             <!-- values are colors, which are integers starting with "#". -->
             <enum name="colorType"   value="3" />
         </attr>
-        <!-- Defines whether the animation should adjust duration in order to achieve the same
-             perceived effects on different devices. -->
-        <attr name="durationScaleHint" >
-            <!-- Default value for scale hint. When set, duration will not be scaled.-->
-            <enum name="noScale" value="0"/>
-            <!-- This should be used when the animation's moving distance is proportional to screen,
-                 as the scaling is based on screen size. -->
-            <enum name="screenBased" value="1"/>
-            <!-- This is for animations that have a distance defined in dp, which will be the same
-                 across different devices. In this case, scaling is based on the physical distance
-                 per dp on the current device. -->
-            <enum name="dpBased" value="2"/>
-        </attr>
+        <!-- Placeholder for a deleted attribute. This should be removed before M release. -->
+        <attr name="removeBeforeMRelease" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="PropertyValuesHolder">
@@ -6099,7 +6088,6 @@
             <!-- child animations should be played sequentially, in the same order as the xml. -->
             <enum name="sequentially" value="1" />
         </attr>
-        <attr name="durationScaleHint" />
     </declare-styleable>
 
     <!-- ========================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index baccafd..875659d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2664,8 +2664,8 @@
   <public type="attr" name="supportsAssistGesture" />
   <public type="attr" name="thumbPosition" />
 
-  <!-- Animation -->
-  <public type="attr" name="durationScaleHint" />
+  <!-- Placeholder for a removed attribute. Remove this before M release. -->
+  <public type="attr" name="removeBeforeMRelease" />
 
   <public type="attr" name="lockTaskMode" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6f554f08..51c2062 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -699,6 +699,12 @@
        discover information about which applications are used on the device.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_manageProfileAndDeviceOwners">Manage profile and device owners</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to set the profile/device owners.
+     [CHAR LIMIT=NONE] -->
+    <string name="permdesc_manageProfileAndDeviceOwners">Allows apps to set the profile owners and the device owner.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_reorderTasks">reorder running apps</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_reorderTasks">Allows the app to move tasks to the
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index db178fa..f81ee8c 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -614,7 +614,7 @@
     <style name="Widget.Material.GestureOverlayView" parent="Widget.GestureOverlayView"/>
 
     <style name="Widget.Material.GridView" parent="Widget.GridView">
-        <item name="listSelector">?attr/selectableItemBackground</item>
+        <item name="listSelector">?attr/listChoiceBackgroundIndicator</item>
     </style>
 
     <style name="Widget.Material.CalendarView" parent="Widget.CalendarView">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e24150..5b13325 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2235,8 +2235,6 @@
   <java-symbol type="dimen" name="floating_toolbar_horizontal_margin" />
   <java-symbol type="dimen" name="floating_toolbar_vertical_margin" />
 
-  <java-symbol type="drawable" name="ic_chevron_left" />
-  <java-symbol type="drawable" name="ic_chevron_right" />
   <java-symbol type="string" name="date_picker_prev_month_button" />
   <java-symbol type="string" name="date_picker_next_month_button" />
   <java-symbol type="layout" name="date_picker_month_item_material" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index a413d91..e8aab07 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -129,8 +129,8 @@
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_material_anim</item>
         <item name="listChoiceIndicatorMultiple">@drawable/btn_check_material_anim</item>
 
-        <item name="listChoiceBackgroundIndicator">@drawable/list_highlight_material</item>
-        <item name="activatedBackgroundIndicator">@null</item>
+        <item name="listChoiceBackgroundIndicator">@drawable/list_choice_background_material</item>
+        <item name="activatedBackgroundIndicator">@drawable/activated_background_material</item>
 
         <item name="listDividerAlertDialog">@null</item>
 
@@ -485,7 +485,7 @@
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_material_anim</item>
         <item name="listChoiceIndicatorMultiple">@drawable/btn_check_material_anim</item>
 
-        <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
+        <item name="listChoiceBackgroundIndicator">@drawable/list_choice_background_material</item>
         <item name="activatedBackgroundIndicator">@drawable/activated_background_material</item>
 
         <item name="expandableListPreferredItemPaddingLeft">40dip</item>
diff --git a/docs/html/google/play-services/index.jd b/docs/html/google/play-services/index.jd
index d674f7f..5ccdcb9 100644
--- a/docs/html/google/play-services/index.jd
+++ b/docs/html/google/play-services/index.jd
@@ -74,30 +74,8 @@
 <a href="http://android-developers.blogspot.com/2015/04/theres-lot-to-explore-with-google-play.html"
 class="external-link">blog post</a>.</p>
 <ul>
-  <li><strong>Maps</strong> - This release makes the Google Maps Android API v2 available on
-<a href="https://developers.google.com/maps/documentation/android/wear" class="external-link">
-Android Wear</a>, so you can now create map-based apps that run directly on wearable devices. In
-addition, the Maps API now offers a new
-<a href="{@docRoot}reference/com/google/android/gms/maps/StreetViewPanorama.OnStreetViewPanoramaLongClickListener.html">
-{@code OnStreetViewPanoramaLongClickListener}</a> interface, similar to the existing
-<a href="{@docRoot}reference/com/google/android/gms/maps/GoogleMap.OnMapLongClickListener.html">
-{@code OnMapLongClickListener}</a> interface. These listeners are particularly helpful
-for wearable devices, so you can let users exit from the app by long-clicking on a map or panorama.
-On a wearable device, the swipe gesture is used to pan the map instead of exiting the app.
-    <ul>
-      <li><a href="https://developers.google.com/maps/documentation/android/wear"
-        class="external-link">Google Maps on Android Wear developer guide</a>
-      </li>
-      <li><a href="https://github.com/googlemaps/android-samples/tree/master/BasicWearMap"
-        class="external-link">Google Maps on Android Wear sample</a>
-      </li>
-      <li><a href="https://developers.google.com/maps/documentation/android/releases"
-        class="external-link">Release notes</a>
-      </li>
-    </ul>
-  </li>
   <li>
-    <strong>Wear</strong> - In addition to Maps support, this release provides you with the ability
+    <strong>Wear</strong> - This release provides you with the ability
 to advertise and discover the capabilities of devices that are connected in a Wear network, through
 the new <a href="{@docRoot}reference/com/google/android/gms/wearable/CapabilityApi.html">
 {@code CapabilityApi}</a> class. The new
diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index 19f510a..2a908ba 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -71,7 +71,7 @@
 </tr>
 
 <tr>
-<td rowspan="11">Audio</td>
+<td rowspan="12">Audio</td>
 <td>AAC LC</td>
 <td style="text-align: center;"><big>&bull;</big></td>
 <td style="text-align: center;"><big>&bull;</big></td>
@@ -180,6 +180,15 @@
 </tr>
 
 <tr>
+<td>Opus</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"><big>&bull;</big><br><small>(Android 5.0+)</small></td>
+<td></td>
+<td>
+  Matroska (.mkv)</td>
+</tr>
+
+<tr>
 <td rowspan="5">Image</td>
 <td>JPEG</td>
 <td style="text-align: center;"><big>&bull;</big></td>
@@ -235,7 +244,7 @@
 
 
 <tr>
-<td rowspan="4">Video</td>
+<td rowspan="6">Video</td>
 <td>H.263</td>
 <td style="text-align: center;"><big>&bull;</big></td>
 <td style="text-align: center;"><big>&bull;</big></td>
@@ -257,6 +266,15 @@
 </tr>
 
 <tr>
+<td>H.265 HEVC</td>
+<td style="text-align: center;" nowrap></td>
+<td style="text-align: center;" nowrap><big>&bull;</big><br><small>(Android 5.0+)</small></td>
+<td>Main Profile Level 3 for mobile devices and Main Profile Level 4.1 for Android TV</td>
+<td>
+  &bull; MPEG-4 (.mp4)<br>
+</tr>
+
+<tr>
 <td>MPEG-4 SP</td>
 <td>&nbsp;</td>
 <td style="text-align: center;"><big>&bull;</big></td>
@@ -275,6 +293,16 @@
   &bull; Matroska (.mkv, Android 4.0+)</td>
 </tr>
 
+<tr>
+<td>VP9</td>
+<td style="text-align: center;" nowrap></td>
+<td style="text-align: center;" nowrap><big>&bull;</big><br><small>(Android 4.4+)</small></td>
+<td></td>
+<td>
+  &bull; <a href="http://www.webmproject.org/">WebM</a> (.webm)<br>
+  &bull; Matroska (.mkv, Android 4.0+)</td>
+</tr>
+
 </tbody></table>
 
 
diff --git a/docs/html/training/tv/start/start.jd b/docs/html/training/tv/start/start.jd
index 2766e90..0f5871f 100644
--- a/docs/html/training/tv/start/start.jd
+++ b/docs/html/training/tv/start/start.jd
@@ -1,4 +1,4 @@
-page.title=Get Started with TV Apps
+page.title=Getting Started with TV Apps
 page.tags="leanback","recyclerview","launcher"
 
 trainingnavtop=true
@@ -10,6 +10,7 @@
 <div id="tb">
   <h2>This lesson teaches you how to</h2>
   <ol>
+    <li><a href="#media">Determine Media Format Support</a></li>
     <li><a href="#dev-project">Setup a TV Project</a></li>
     <li><a href="#tv-libraries">Add TV Support Libraries</a></li>
     <li><a href="#build-it">Build TV Apps</a></li>
@@ -42,6 +43,18 @@
   minimum required changes to enable an app to run on TV devices.
 </p>
 
+<h2 id="media">Determine Media Format Support</h2>
+
+<p>See the following documentation for information about the codecs, protocols, and formats
+supported by Android TV.</p>
+
+<ul>
+  <li><a href="{@docRoot}guide/appendix/media-formats.html">Supported Media Formats</a></li>
+  <li><a class="external-link" href="https://source.android.com/devices/drm.html">DRM</a></li>
+  <li><code><a href="{@docRoot}reference/android/drm/package-summary.html">android.drm</a></code></li>
+  <li><a href="{@docRoot}guide/topics/media/exoplayer.html">ExoPlayer</a></li>
+  <li>{@link android.media.MediaPlayer android.media.MediaPlayer}</li>
+</ul>
 
 <h2 id="dev-project">Set up a TV Project</h2>
 
@@ -284,9 +297,15 @@
     TV devices.
   </li>
   <li>
-    <a href="{@docRoot}training/tv/games/index.html">Games for TV</a> - TV devices are a great
+    <a href="{@docRoot}training/tv/games/index.html">Building TV Games</a> - TV devices are a great
     platform for games. See this topic for information on building great game experiences for TV.
   </li>
+    <li>
+    <a href="{@docRoot}training/tv/tif/index.html">Building Live TV Apps</a> - Present your video
+    content in a linear, "broadcast TV" style with channels and programs that your users can access
+    through a program guide as well as the channel up/down buttons.
+  </li>
+
 </ul>
 
 
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 5fae831..3b25ba6 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -17,7 +17,7 @@
 package android.security;
 
 import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
-import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.NativeConstants;
 import com.android.org.conscrypt.OpenSSLEngine;
 
 import java.security.InvalidAlgorithmParameterException;
@@ -206,9 +206,9 @@
     }
 
     private static int getDefaultKeySize(int keyType) {
-        if (keyType == NativeCrypto.EVP_PKEY_EC) {
+        if (keyType == NativeConstants.EVP_PKEY_EC) {
             return EC_DEFAULT_KEY_SIZE;
-        } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
+        } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
             return RSA_DEFAULT_KEY_SIZE;
         }
         return -1;
@@ -216,12 +216,12 @@
 
     private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize)
             throws InvalidAlgorithmParameterException {
-        if (keyType == NativeCrypto.EVP_PKEY_EC) {
+        if (keyType == NativeConstants.EVP_PKEY_EC) {
             if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
                 throw new InvalidAlgorithmParameterException("EC keys must be >= "
                         + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
             }
-        } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
+        } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
             if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
                 throw new InvalidAlgorithmParameterException("RSA keys must be >= "
                         + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
@@ -234,7 +234,7 @@
 
     private static void checkCorrectParametersSpec(int keyType, int keySize,
             AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
-        if (keyType == NativeCrypto.EVP_PKEY_RSA && spec != null) {
+        if (keyType == NativeConstants.EVP_PKEY_RSA && spec != null) {
             if (spec instanceof RSAKeyGenParameterSpec) {
                 RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
                 if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
@@ -260,7 +260,7 @@
 
     private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) {
         switch (keyType) {
-            case NativeCrypto.EVP_PKEY_RSA:
+            case NativeConstants.EVP_PKEY_RSA:
                 if (spec instanceof RSAKeyGenParameterSpec) {
                     RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
                     return new byte[][] { rsaSpec.getPublicExponent().toByteArray() };
diff --git a/keystore/java/android/security/CryptoOperationException.java b/keystore/java/android/security/CryptoOperationException.java
deleted file mode 100644
index 1c9d005..0000000
--- a/keystore/java/android/security/CryptoOperationException.java
+++ /dev/null
@@ -1,59 +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 android.security;
-
-/**
- * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
- * exception.
- *
- * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
- * {@code Signature}) is that they can throw a checked exception during initialization, but are not
- * permitted to throw a checked exception during operation. Because crypto operations can fail
- * for a variety of reasons after initialization, this base class provides type-safety for unchecked
- * exceptions that may be thrown in those cases.
- */
-public class CryptoOperationException extends RuntimeException {
-
-    /**
-     * Constructs a new {@code CryptoOperationException} without detail message and cause.
-     */
-    public CryptoOperationException() {
-        super();
-    }
-
-    /**
-     * Constructs a new {@code CryptoOperationException} with the provided detail message and no
-     * cause.
-     */
-    public CryptoOperationException(String message) {
-        super(message);
-    }
-
-    /**
-     * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
-     */
-    public CryptoOperationException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    /**
-     * Constructs a new {@code CryptoOperationException} with the provided cause.
-     */
-    public CryptoOperationException(Throwable cause) {
-        super(cause);
-    }
-}
diff --git a/keystore/java/android/security/KeyExpiredException.java b/keystore/java/android/security/KeyExpiredException.java
index a02dc33..f58e48a 100644
--- a/keystore/java/android/security/KeyExpiredException.java
+++ b/keystore/java/android/security/KeyExpiredException.java
@@ -16,11 +16,13 @@
 
 package android.security;
 
+import java.security.InvalidKeyException;
+
 /**
  * Indicates that a cryptographic operation failed because the employed key's validity end date
  * is in the past.
  */
-public class KeyExpiredException extends CryptoOperationException {
+public class KeyExpiredException extends InvalidKeyException {
 
     /**
      * Constructs a new {@code KeyExpiredException} without detail message and cause.
diff --git a/keystore/java/android/security/KeyNotYetValidException.java b/keystore/java/android/security/KeyNotYetValidException.java
index 964cd7e..4ea27ef 100644
--- a/keystore/java/android/security/KeyNotYetValidException.java
+++ b/keystore/java/android/security/KeyNotYetValidException.java
@@ -16,11 +16,13 @@
 
 package android.security;
 
+import java.security.InvalidKeyException;
+
 /**
  * Indicates that a cryptographic operation failed because the employed key's validity start date
  * is in the future.
  */
-public class KeyNotYetValidException extends CryptoOperationException {
+public class KeyNotYetValidException extends InvalidKeyException {
 
     /**
      * Constructs a new {@code KeyNotYetValidException} without detail message and cause.
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 5af0527..8c49ff0 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -16,7 +16,7 @@
 
 package android.security;
 
-import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.NativeConstants;
 
 import android.os.Binder;
 import android.os.IBinder;
@@ -30,6 +30,7 @@
 import android.security.keymaster.OperationResult;
 import android.util.Log;
 
+import java.security.InvalidKeyException;
 import java.util.Locale;
 
 /**
@@ -87,9 +88,9 @@
 
     static int getKeyTypeForAlgorithm(String keyType) {
         if ("RSA".equalsIgnoreCase(keyType)) {
-            return NativeCrypto.EVP_PKEY_RSA;
+            return NativeConstants.EVP_PKEY_RSA;
         } else if ("EC".equalsIgnoreCase(keyType)) {
-            return NativeCrypto.EVP_PKEY_EC;
+            return NativeConstants.EVP_PKEY_EC;
         } else {
             return -1;
         }
@@ -508,7 +509,11 @@
         }
     }
 
-    public static KeyStoreException getKeyStoreException(int errorCode) {
+    /**
+     * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
+     * code.
+     */
+    static KeyStoreException getKeyStoreException(int errorCode) {
         if (errorCode > 0) {
             // KeyStore layer error
             switch (errorCode) {
@@ -544,7 +549,11 @@
         }
     }
 
-    public static CryptoOperationException getCryptoOperationException(KeyStoreException e) {
+    /**
+     * Returns an {@link InvalidKeyException} corresponding to the provided
+     * {@link KeyStoreException}.
+     */
+    static InvalidKeyException getInvalidKeyException(KeyStoreException e) {
         switch (e.getErrorCode()) {
             case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
                 return new KeyExpiredException();
@@ -556,11 +565,15 @@
             // case KeymasterDefs.KM_ERROR_TBD
             //     return new NewFingerprintEnrolledException();
             default:
-                return new CryptoOperationException("Crypto operation failed", e);
+                return new InvalidKeyException("Keystore operation failed", e);
         }
     }
 
-    public static CryptoOperationException getCryptoOperationException(int errorCode) {
-        return getCryptoOperationException(getKeyStoreException(errorCode));
+    /**
+     * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
+     * code.
+     */
+    static InvalidKeyException getInvalidKeyException(int errorCode) {
+        return getInvalidKeyException(getKeyStoreException(errorCode));
     }
 }
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 37e00b2..3b13e83 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -136,6 +136,14 @@
     private Long mOperationHandle;
     private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
 
+    /**
+     * Encountered exception which could not be immediately thrown because it was encountered inside
+     * a method that does not throw checked exception. This exception will be thrown from
+     * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and
+     * {@code engineDoFinal} start ignoring input data.
+     */
+    private Exception mCachedException;
+
     protected KeyStoreCipherSpi(
             int keymasterAlgorithm,
             int keymasterBlockMode,
@@ -152,29 +160,62 @@
 
     @Override
     protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
-        init(opmode, key, random);
-        initAlgorithmSpecificParameters();
-        ensureKeystoreOperationInitialized();
+        resetAll();
+
+        boolean success = false;
+        try {
+            init(opmode, key, random);
+            initAlgorithmSpecificParameters();
+            try {
+                ensureKeystoreOperationInitialized();
+            } catch (InvalidAlgorithmParameterException e) {
+                throw new InvalidKeyException(e);
+            }
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
     }
 
     @Override
     protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
             throws InvalidKeyException, InvalidAlgorithmParameterException {
-        init(opmode, key, random);
-        initAlgorithmSpecificParameters(params);
-        ensureKeystoreOperationInitialized();
+        resetAll();
+
+        boolean success = false;
+        try {
+            init(opmode, key, random);
+            initAlgorithmSpecificParameters(params);
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
     }
 
     @Override
     protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
             SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
-        init(opmode, key, random);
-        initAlgorithmSpecificParameters(params);
-        ensureKeystoreOperationInitialized();
+        resetAll();
+
+        boolean success = false;
+        try {
+            init(opmode, key, random);
+            initAlgorithmSpecificParameters(params);
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
     }
 
     private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
-        resetAll();
         if (!(key instanceof KeyStoreSecretKey)) {
             throw new InvalidKeyException(
                     "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
@@ -207,6 +248,7 @@
         mOperationToken = null;
         mOperationHandle = null;
         mMainDataStreamer = null;
+        mCachedException = null;
     }
 
     private void resetWhilePreservingInitState() {
@@ -218,12 +260,17 @@
         mOperationHandle = null;
         mMainDataStreamer = null;
         mAdditionalEntropyForBegin = null;
+        mCachedException = null;
     }
 
-    private void ensureKeystoreOperationInitialized() {
+    private void ensureKeystoreOperationInitialized() throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
         if (mMainDataStreamer != null) {
             return;
         }
+        if (mCachedException != null) {
+            return;
+        }
         if (mKey == null) {
             throw new IllegalStateException("Not initialized");
         }
@@ -252,11 +299,15 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw KeyStore.getCryptoOperationException(opResult.resultCode);
+            switch (opResult.resultCode) {
+                case KeymasterDefs.KM_ERROR_INVALID_NONCE:
+                    throw new InvalidAlgorithmParameterException("Invalid IV");
+            }
+            throw KeyStore.getInvalidKeyException(opResult.resultCode);
         }
 
         if (opResult.token == null) {
-            throw new CryptoOperationException("Keystore returned null operation token");
+            throw new IllegalStateException("Keystore returned null operation token");
         }
         mOperationToken = opResult.token;
         mOperationHandle = opResult.operationHandle;
@@ -270,7 +321,15 @@
 
     @Override
     protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
-        ensureKeystoreOperationInitialized();
+        if (mCachedException != null) {
+            return null;
+        }
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            mCachedException = e;
+            return null;
+        }
 
         if (inputLen == 0) {
             return null;
@@ -280,7 +339,8 @@
         try {
             output = mMainDataStreamer.update(input, inputOffset, inputLen);
         } catch (KeyStoreException e) {
-            throw KeyStore.getCryptoOperationException(e);
+            mCachedException = e;
+            return null;
         }
 
         if (output.length == 0) {
@@ -309,7 +369,16 @@
     @Override
     protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
             throws IllegalBlockSizeException, BadPaddingException {
-        ensureKeystoreOperationInitialized();
+        if (mCachedException != null) {
+            throw (IllegalBlockSizeException)
+                    new IllegalBlockSizeException().initCause(mCachedException);
+        }
+
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+            throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+        }
 
         byte[] output;
         try {
@@ -323,7 +392,7 @@
                 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
                     throw new AEADBadTagException();
                 default:
-                    throw KeyStore.getCryptoOperationException(e);
+                    throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
             }
         }
 
@@ -584,11 +653,11 @@
             if (mIv == null) {
                 mIv = returnedIv;
             } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
-                throw new CryptoOperationException("IV in use differs from provided IV");
+                throw new IllegalStateException("IV in use differs from provided IV");
             }
         } else {
             if (returnedIv != null) {
-                throw new CryptoOperationException(
+                throw new IllegalStateException(
                         "IV in use despite IV not being used by this transformation");
             }
         }
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
index 8ed6e04..1aa3aec 100644
--- a/keystore/java/android/security/KeyStoreConnectException.java
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -21,7 +21,7 @@
  *
  * @hide
  */
-public class KeyStoreConnectException extends CryptoOperationException {
+public class KeyStoreConnectException extends IllegalStateException {
     public KeyStoreConnectException() {
         super("Failed to communicate with keystore service");
     }
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
index aafd2fa..0619199 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -136,7 +136,7 @@
                     // More input is available, but it wasn't included into the previous chunk
                     // because the chunk reached its maximum permitted size.
                     // Shouldn't have happened.
-                    throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
+                    throw new IllegalStateException("Nothing consumed from max-sized chunk: "
                             + chunk.length + " bytes");
                 }
                 mBuffered = chunk;
@@ -148,7 +148,7 @@
                 mBufferedOffset = opResult.inputConsumed;
                 mBufferedLength = chunk.length - opResult.inputConsumed;
             } else {
-                throw new CryptoOperationException("Consumed more than provided: "
+                throw new IllegalStateException("Consumed more than provided: "
                         + opResult.inputConsumed + ", provided: " + chunk.length);
             }
 
@@ -160,7 +160,7 @@
                         try {
                             bufferedOutput.write(opResult.output);
                         } catch (IOException e) {
-                            throw new CryptoOperationException("Failed to buffer output", e);
+                            throw new IllegalStateException("Failed to buffer output", e);
                         }
                     }
                 } else {
@@ -173,7 +173,7 @@
                         try {
                             bufferedOutput.write(opResult.output);
                         } catch (IOException e) {
-                            throw new CryptoOperationException("Failed to buffer output", e);
+                            throw new IllegalStateException("Failed to buffer output", e);
                         }
                         return bufferedOutput.toByteArray();
                     }
@@ -233,10 +233,10 @@
         }
 
         if (opResult.inputConsumed < chunk.length) {
-            throw new CryptoOperationException("Keystore failed to consume all input. Provided: "
+            throw new IllegalStateException("Keystore failed to consume all input. Provided: "
                     + chunk.length + ", consumed: " + opResult.inputConsumed);
         } else if (opResult.inputConsumed > chunk.length) {
-            throw new CryptoOperationException("Keystore consumed more input than provided"
+            throw new IllegalStateException("Keystore consumed more input than provided"
                     + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
         }
 
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index a19bbda..175369c 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -69,9 +69,10 @@
     private final int mKeymasterDigest;
     private final int mMacSizeBytes;
 
-    private String mKeyAliasInKeyStore;
+    // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+    private KeyStoreSecretKey mKey;
 
-    // The fields below are reset by the engineReset operation.
+    // Fields below are reset when engineDoFinal succeeds.
     private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
     private IBinder mOperationToken;
     private Long mOperationHandle;
@@ -89,28 +90,39 @@
     @Override
     protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
             InvalidAlgorithmParameterException {
+        resetAll();
+
+        boolean success = false;
+        try {
+            init(key, params);
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    private void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+        InvalidAlgorithmParameterException {
         if (key == null) {
             throw new InvalidKeyException("key == null");
         } else if (!(key instanceof KeyStoreSecretKey)) {
             throw new InvalidKeyException(
                     "Only Android KeyStore secret keys supported. Key: " + key);
         }
+        mKey = (KeyStoreSecretKey) key;
 
         if (params != null) {
             throw new InvalidAlgorithmParameterException(
                     "Unsupported algorithm parameters: " + params);
         }
 
-        mKeyAliasInKeyStore = ((KeyStoreSecretKey) key).getAlias();
-        if (mKeyAliasInKeyStore == null) {
-            throw new InvalidKeyException("Key's KeyStore alias not known");
-        }
-        engineReset();
-        ensureKeystoreOperationInitialized();
     }
 
-    @Override
-    protected void engineReset() {
+    private void resetAll() {
+        mKey = null;
         IBinder operationToken = mOperationToken;
         if (operationToken != null) {
             mOperationToken = null;
@@ -120,11 +132,26 @@
         mChunkedStreamer = null;
     }
 
-    private void ensureKeystoreOperationInitialized() {
+    private void resetWhilePreservingInitState() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mOperationHandle = null;
+        mChunkedStreamer = null;
+    }
+
+    @Override
+    protected void engineReset() {
+        resetWhilePreservingInitState();
+    }
+
+    private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
         if (mChunkedStreamer != null) {
             return;
         }
-        if (mKeyAliasInKeyStore == null) {
+        if (mKey == null) {
             throw new IllegalStateException("Not initialized");
         }
 
@@ -132,7 +159,8 @@
         keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
         keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
 
-        OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore,
+        OperationResult opResult = mKeyStore.begin(
+                mKey.getAlias(),
                 KeymasterDefs.KM_PURPOSE_SIGN,
                 true,
                 keymasterArgs,
@@ -141,10 +169,10 @@
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-            throw KeyStore.getCryptoOperationException(opResult.resultCode);
+            throw KeyStore.getInvalidKeyException(opResult.resultCode);
         }
         if (opResult.token == null) {
-            throw new CryptoOperationException("Keystore returned null operation token");
+            throw new IllegalStateException("Keystore returned null operation token");
         }
         mOperationToken = opResult.token;
         mOperationHandle = opResult.operationHandle;
@@ -160,31 +188,39 @@
 
     @Override
     protected void engineUpdate(byte[] input, int offset, int len) {
-        ensureKeystoreOperationInitialized();
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new IllegalStateException("Failed to reinitialize MAC", e);
+        }
 
         byte[] output;
         try {
             output = mChunkedStreamer.update(input, offset, len);
         } catch (KeyStoreException e) {
-            throw KeyStore.getCryptoOperationException(e);
+            throw new IllegalStateException("Keystore operation failed", e);
         }
         if ((output != null) && (output.length != 0)) {
-            throw new CryptoOperationException("Update operation unexpectedly produced output");
+            throw new IllegalStateException("Update operation unexpectedly produced output");
         }
     }
 
     @Override
     protected byte[] engineDoFinal() {
-        ensureKeystoreOperationInitialized();
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new IllegalStateException("Failed to reinitialize MAC", e);
+        }
 
         byte[] result;
         try {
             result = mChunkedStreamer.doFinal(null, 0, 0);
         } catch (KeyStoreException e) {
-            throw KeyStore.getCryptoOperationException(e);
+            throw new IllegalStateException("Keystore operation failed", e);
         }
 
-        engineReset();
+        resetWhilePreservingInitState();
         return result;
     }
 
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index d1abe12..293c4c9 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -210,7 +210,8 @@
         int errorCode = mKeyStore.generateKey(
                 keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
         if (errorCode != KeyStore.NO_ERROR) {
-            throw KeyStore.getCryptoOperationException(errorCode);
+            throw new IllegalStateException(
+                    "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
         }
         String keyAlgorithmJCA =
                 KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest);
diff --git a/keystore/java/android/security/NewFingerprintEnrolledException.java b/keystore/java/android/security/NewFingerprintEnrolledException.java
index 806b214..4fe210b 100644
--- a/keystore/java/android/security/NewFingerprintEnrolledException.java
+++ b/keystore/java/android/security/NewFingerprintEnrolledException.java
@@ -16,11 +16,13 @@
 
 package android.security;
 
+import java.security.InvalidKeyException;
+
 /**
  * Indicates that a cryptographic operation could not be performed because the key used by the
  * operation is permanently invalid because a new fingerprint was enrolled.
  */
-public class NewFingerprintEnrolledException extends CryptoOperationException {
+public class NewFingerprintEnrolledException extends InvalidKeyException {
 
     /**
      * Constructs a new {@code NewFingerprintEnrolledException} without detail message and cause.
diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java
index f5f5f41..66f4dd8 100644
--- a/keystore/java/android/security/UserNotAuthenticatedException.java
+++ b/keystore/java/android/security/UserNotAuthenticatedException.java
@@ -16,11 +16,13 @@
 
 package android.security;
 
+import java.security.InvalidKeyException;
+
 /**
  * Indicates that a cryptographic operation could not be performed because the user has not been
  * authenticated recently enough.
  */
-public class UserNotAuthenticatedException extends CryptoOperationException {
+public class UserNotAuthenticatedException extends InvalidKeyException {
 
     /**
      * Constructs a new {@code UserNotAuthenticatedException} without detail message and cause.
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
index 7a88dee..a7046dd2 100644
--- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -18,7 +18,7 @@
 
 import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
 
-import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.NativeConstants;
 import com.android.org.conscrypt.OpenSSLEngine;
 
 import android.test.AndroidTestCase;
@@ -768,7 +768,7 @@
         assertAliases(new String[] {});
 
         assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
-                KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+                KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
                 null));
 
         assertAliases(new String[] { TEST_ALIAS_1 });
@@ -797,7 +797,7 @@
         assertAliases(new String[] {});
 
         assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
-                KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+                KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
                 null));
 
         assertTrue("Should contain generated private key", mKeyStore.containsAlias(TEST_ALIAS_1));
@@ -1963,7 +1963,7 @@
         {
             final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
             assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
-                    NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+                    NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
 
             Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
 
@@ -2019,7 +2019,7 @@
         {
             final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
             assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
-                    NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+                    NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
 
             X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1,
                     TEST_SERIAL_1, TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
@@ -2032,7 +2032,7 @@
         {
             final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_2;
             assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
-                    NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+                    NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
 
             X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_2,
                     TEST_SERIAL_2, TEST_DN_2, NOW, NOW_PLUS_10_YEARS);
@@ -2064,7 +2064,7 @@
         {
             final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
             assertTrue(mAndroidKeyStore.generate(privateKeyAlias,
-                    android.security.KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024,
+                    android.security.KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024,
                     android.security.KeyStore.FLAG_NONE, null));
 
             X509Certificate cert =
@@ -2116,7 +2116,7 @@
         assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
 
         assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3,
-                KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+                KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
                 null));
 
         assertEquals("The keystore size should match expected", 3, mKeyStore.size());
@@ -2184,7 +2184,7 @@
     private void setupKey() throws Exception {
         final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
         assertTrue(mAndroidKeyStore
-                .generate(privateKeyAlias, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024,
+                .generate(privateKeyAlias, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024,
                         KeyStore.FLAG_ENCRYPTED, null));
 
         X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1,
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 1a5552a..916b1ba 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -32,7 +32,7 @@
 import android.test.AssertionFailedError;
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.MediumTest;
-import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.NativeConstants;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Date;
@@ -365,7 +365,7 @@
 
     public void testGenerate_NotInitialized_Fail() throws Exception {
         assertFalse("Should fail when keystore is not initialized",
-                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
     }
 
@@ -373,7 +373,7 @@
         mKeyStore.password(TEST_PASSWD);
         mKeyStore.lock();
         assertFalse("Should fail when keystore is locked",
-                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
     }
 
@@ -381,7 +381,7 @@
         assertTrue(mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key when unlocked",
-                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
         assertTrue(mKeyStore.contains(TEST_KEYNAME));
         assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
@@ -391,7 +391,7 @@
         assertTrue(mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key when unlocked",
-                mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
         assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
         assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -401,7 +401,7 @@
         assertTrue(mKeyStore.password(TEST_PASSWD));
 
         assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID,
-                    NativeCrypto.EVP_PKEY_RSA, RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+                    NativeConstants.EVP_PKEY_RSA, RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
         assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
         assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
         assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -447,7 +447,7 @@
     public void testSign_Success() throws Exception {
         mKeyStore.password(TEST_PASSWD);
 
-        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                     RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
         assertTrue(mKeyStore.contains(TEST_KEYNAME));
         final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
@@ -458,7 +458,7 @@
     public void testVerify_Success() throws Exception {
         mKeyStore.password(TEST_PASSWD);
 
-        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                     RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
         assertTrue(mKeyStore.contains(TEST_KEYNAME));
         final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
@@ -486,7 +486,7 @@
                 mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key for testcase",
-                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
 
         assertTrue("Should be able to grant key to other user",
@@ -520,7 +520,7 @@
                 mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key for testcase",
-                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
 
         assertTrue("Should be able to grant key to other user",
@@ -554,7 +554,7 @@
                 mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key for testcase",
-                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
 
         assertFalse("Should not be able to revoke not existent grant",
@@ -566,7 +566,7 @@
                 mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key for testcase",
-                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
 
         assertTrue("Should be able to grant key to other user",
@@ -584,7 +584,7 @@
                 mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key for testcase",
-                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+                mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                         RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
 
         assertTrue("Should be able to grant key to other user",
@@ -605,7 +605,7 @@
 
         assertFalse(mKeyStore.contains(TEST_KEYNAME));
 
-        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                     RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
 
         assertTrue(mKeyStore.contains(TEST_KEYNAME));
@@ -644,7 +644,7 @@
 
         assertFalse(mKeyStore.contains(TEST_KEYNAME));
 
-        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+        assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
                     RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
 
         assertTrue(mKeyStore.contains(TEST_KEYNAME));
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index 5fca8ec..836f868 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -24,6 +24,7 @@
     thread/TaskManager.cpp \
     utils/Blur.cpp \
     utils/GLUtils.cpp \
+    utils/LinearAllocator.cpp \
     utils/SortedListImpl.cpp \
     AmbientShadow.cpp \
     AnimationContext.cpp \
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index c92ab91..f535afb 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -127,7 +127,7 @@
     }
 
     void tryRecycleState(DeferredDisplayState* state) {
-        mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState));
+        mAllocator.rewindIfLastAlloc(state);
     }
 
     /**
diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk
index 51898d2..b6f0baf 100644
--- a/libs/hwui/tests/Android.mk
+++ b/libs/hwui/tests/Android.mk
@@ -34,16 +34,3 @@
 	tests/main.cpp
 
 include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
-LOCAL_MODULE := hwui_unit_tests
-LOCAL_MODULE_TAGS := tests
-
-include $(LOCAL_PATH)/Android.common.mk
-
-LOCAL_SRC_FILES += \
-	tests/ClipAreaTests.cpp \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk
new file mode 100644
index 0000000..51601b0
--- /dev/null
+++ b/libs/hwui/unit_tests/Android.mk
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+local_target_dir := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PATH:= $(call my-dir)/..
+
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
+LOCAL_MODULE := hwui_unit_tests
+LOCAL_MODULE_TAGS := tests
+
+include $(LOCAL_PATH)/Android.common.mk
+
+LOCAL_SRC_FILES += \
+    unit_tests/ClipAreaTests.cpp \
+    unit_tests/LinearAllocatorTests.cpp \
+    unit_tests/main.cpp
+
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp
similarity index 100%
rename from libs/hwui/tests/ClipAreaTests.cpp
rename to libs/hwui/unit_tests/ClipAreaTests.cpp
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
new file mode 100644
index 0000000..b3959d1
--- /dev/null
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <utils/LinearAllocator.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+struct SimplePair {
+    int one = 1;
+    int two = 2;
+};
+
+class SignalingDtor {
+public:
+    SignalingDtor() {
+        mDestroyed = nullptr;
+    }
+    SignalingDtor(bool* destroyedSignal) {
+        mDestroyed = destroyedSignal;
+        *mDestroyed = false;
+    }
+    virtual ~SignalingDtor() {
+        if (mDestroyed) {
+            *mDestroyed = true;
+        }
+    }
+    void setSignal(bool* destroyedSignal) {
+        mDestroyed = destroyedSignal;
+    }
+private:
+    bool* mDestroyed;
+};
+
+TEST(LinearAllocator, alloc) {
+    LinearAllocator la;
+    EXPECT_EQ(0u, la.usedSize());
+    la.alloc(64);
+    // There's some internal tracking as well as padding
+    // so the usedSize isn't strictly defined
+    EXPECT_LE(64u, la.usedSize());
+    EXPECT_GT(80u, la.usedSize());
+    auto pair = la.alloc<SimplePair>();
+    EXPECT_LE(64u + sizeof(SimplePair), la.usedSize());
+    EXPECT_GT(80u + sizeof(SimplePair), la.usedSize());
+    EXPECT_EQ(1, pair->one);
+    EXPECT_EQ(2, pair->two);
+}
+
+TEST(LinearAllocator, dtor) {
+    bool destroyed[10];
+    {
+        LinearAllocator la;
+        for (int i = 0; i < 5; i++) {
+            la.alloc<SignalingDtor>()->setSignal(destroyed + i);
+            la.alloc<SimplePair>();
+        }
+        la.alloc(100);
+        for (int i = 0; i < 5; i++) {
+            auto sd = new (la) SignalingDtor(destroyed + 5 + i);
+            la.autoDestroy(sd);
+            new (la) SimplePair();
+        }
+        la.alloc(100);
+        for (int i = 0; i < 10; i++) {
+            EXPECT_FALSE(destroyed[i]);
+        }
+    }
+    for (int i = 0; i < 10; i++) {
+        EXPECT_TRUE(destroyed[i]);
+    }
+}
+
+TEST(LinearAllocator, rewind) {
+    bool destroyed;
+    {
+        LinearAllocator la;
+        auto addr = la.alloc(100);
+        EXPECT_LE(100u, la.usedSize());
+        la.rewindIfLastAlloc(addr, 100);
+        EXPECT_GT(16u, la.usedSize());
+        size_t emptySize = la.usedSize();
+        auto sigdtor = la.alloc<SignalingDtor>();
+        sigdtor->setSignal(&destroyed);
+        EXPECT_FALSE(destroyed);
+        EXPECT_LE(emptySize, la.usedSize());
+        la.rewindIfLastAlloc(sigdtor);
+        EXPECT_TRUE(destroyed);
+        EXPECT_EQ(emptySize, la.usedSize());
+        destroyed = false;
+    }
+    // Checking for a double-destroy case
+    EXPECT_EQ(destroyed, false);
+}
diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt
new file mode 100755
index 0000000..a2d6a34
--- /dev/null
+++ b/libs/hwui/unit_tests/how_to_run.txt
@@ -0,0 +1,4 @@
+mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests &&
+adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \
+    /data/nativetest/hwui_unit_tests/hwui_unit_tests &&
+adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests
diff --git a/libs/hwui/unit_tests/main.cpp b/libs/hwui/unit_tests/main.cpp
new file mode 100644
index 0000000..c9b9636
--- /dev/null
+++ b/libs/hwui/unit_tests/main.cpp
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
new file mode 100644
index 0000000..59b12cf
--- /dev/null
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_NDEBUG 1
+
+#include "utils/LinearAllocator.h"
+
+#include <stdlib.h>
+#include <utils/Log.h>
+
+
+// The ideal size of a page allocation (these need to be multiples of 8)
+#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
+#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
+
+// The maximum amount of wasted space we can have per page
+// Allocations exceeding this will have their own dedicated page
+// If this is too low, we will malloc too much
+// Too high, and we may waste too much space
+// Must be smaller than INITIAL_PAGE_SIZE
+#define MAX_WASTE_SIZE ((size_t)1024)
+
+#if ALIGN_DOUBLE
+#define ALIGN_SZ (sizeof(double))
+#else
+#define ALIGN_SZ (sizeof(int))
+#endif
+
+#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1))
+#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
+
+#if LOG_NDEBUG
+#define ADD_ALLOCATION(size)
+#define RM_ALLOCATION(size)
+#else
+#include <utils/Thread.h>
+#include <utils/Timers.h>
+static size_t s_totalAllocations = 0;
+static nsecs_t s_nextLog = 0;
+static android::Mutex s_mutex;
+
+static void _logUsageLocked() {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    if (now > s_nextLog) {
+        s_nextLog = now + milliseconds_to_nanoseconds(10);
+        ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+    }
+}
+
+static void _addAllocation(size_t size) {
+    android::AutoMutex lock(s_mutex);
+    s_totalAllocations += size;
+    _logUsageLocked();
+}
+
+#define ADD_ALLOCATION(size) _addAllocation(size);
+#define RM_ALLOCATION(size) _addAllocation(-size);
+#endif
+
+#define min(x,y) (((x) < (y)) ? (x) : (y))
+
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) {
+    return la.alloc(size);
+}
+
+namespace android {
+namespace uirenderer {
+
+class LinearAllocator::Page {
+public:
+    Page* next() { return mNextPage; }
+    void setNext(Page* next) { mNextPage = next; }
+
+    Page()
+        : mNextPage(0)
+    {}
+
+    void* operator new(size_t /*size*/, void* buf) { return buf; }
+
+    void* start() {
+        return (void*) (((size_t)this) + sizeof(Page));
+    }
+
+    void* end(int pageSize) {
+        return (void*) (((size_t)start()) + pageSize);
+    }
+
+private:
+    Page(const Page& /*other*/) {}
+    Page* mNextPage;
+};
+
+LinearAllocator::LinearAllocator()
+    : mPageSize(INITIAL_PAGE_SIZE)
+    , mMaxAllocSize(MAX_WASTE_SIZE)
+    , mNext(0)
+    , mCurrentPage(0)
+    , mPages(0)
+    , mTotalAllocated(0)
+    , mWastedSpace(0)
+    , mPageCount(0)
+    , mDedicatedPageCount(0) {}
+
+LinearAllocator::~LinearAllocator(void) {
+    while (mDtorList) {
+        auto node = mDtorList;
+        mDtorList = node->next;
+        node->dtor(node->addr);
+    }
+    Page* p = mPages;
+    while (p) {
+        Page* next = p->next();
+        p->~Page();
+        free(p);
+        RM_ALLOCATION(mPageSize);
+        p = next;
+    }
+}
+
+void* LinearAllocator::start(Page* p) {
+    return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+}
+
+void* LinearAllocator::end(Page* p) {
+    return ((char*)p) + mPageSize;
+}
+
+bool LinearAllocator::fitsInCurrentPage(size_t size) {
+    return mNext && ((char*)mNext + size) <= end(mCurrentPage);
+}
+
+void LinearAllocator::ensureNext(size_t size) {
+    if (fitsInCurrentPage(size)) return;
+
+    if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
+        mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+        mPageSize = ALIGN(mPageSize);
+    }
+    mWastedSpace += mPageSize;
+    Page* p = newPage(mPageSize);
+    if (mCurrentPage) {
+        mCurrentPage->setNext(p);
+    }
+    mCurrentPage = p;
+    if (!mPages) {
+        mPages = mCurrentPage;
+    }
+    mNext = start(mCurrentPage);
+}
+
+void* LinearAllocator::alloc(size_t size) {
+    size = ALIGN(size);
+    if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
+        ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
+        // Allocation is too large, create a dedicated page for the allocation
+        Page* page = newPage(size);
+        mDedicatedPageCount++;
+        page->setNext(mPages);
+        mPages = page;
+        if (!mCurrentPage)
+            mCurrentPage = mPages;
+        return start(page);
+    }
+    ensureNext(size);
+    void* ptr = mNext;
+    mNext = ((char*)mNext) + size;
+    mWastedSpace -= size;
+    return ptr;
+}
+
+void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) {
+    static_assert(std::is_standard_layout<DestructorNode>::value,
+                  "DestructorNode must have standard layout");
+    static_assert(std::is_trivially_destructible<DestructorNode>::value,
+                  "DestructorNode must be trivially destructable");
+    auto node = new (*this) DestructorNode();
+    node->dtor = dtor;
+    node->addr = addr;
+    node->next = mDtorList;
+    mDtorList = node;
+}
+
+void LinearAllocator::runDestructorFor(void* addr) {
+    auto node = mDtorList;
+    DestructorNode* previous = nullptr;
+    while (node) {
+        if (node->addr == addr) {
+            if (previous) {
+                previous->next = node->next;
+            } else {
+                mDtorList = node->next;
+            }
+            node->dtor(node->addr);
+            rewindIfLastAlloc(node, sizeof(DestructorNode));
+            break;
+        }
+        previous = node;
+        node = node->next;
+    }
+}
+
+void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
+    // First run the destructor as running the destructor will
+    // also rewind for the DestructorNode allocation which will
+    // have been allocated after this void* if it has a destructor
+    runDestructorFor(ptr);
+    // Don't bother rewinding across pages
+    allocSize = ALIGN(allocSize);
+    if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
+            && ptr == ((char*)mNext - allocSize)) {
+        mWastedSpace += allocSize;
+        mNext = ptr;
+    }
+}
+
+LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
+    pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
+    ADD_ALLOCATION(pageSize);
+    mTotalAllocated += pageSize;
+    mPageCount++;
+    void* buf = malloc(pageSize);
+    return new (buf) Page();
+}
+
+static const char* toSize(size_t value, float& result) {
+    if (value < 2000) {
+        result = value;
+        return "B";
+    }
+    if (value < 2000000) {
+        result = value / 1024.0f;
+        return "KB";
+    }
+    result = value / 1048576.0f;
+    return "MB";
+}
+
+void LinearAllocator::dumpMemoryStats(const char* prefix) {
+    float prettySize;
+    const char* prettySuffix;
+    prettySuffix = toSize(mTotalAllocated, prettySize);
+    ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix);
+    prettySuffix = toSize(mWastedSpace, prettySize);
+    ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix,
+          (float) mWastedSpace / (float) mTotalAllocated * 100.0f);
+    ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
new file mode 100644
index 0000000..d90dd82
--- /dev/null
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_LINEARALLOCATOR_H
+#define ANDROID_LINEARALLOCATOR_H
+
+#include <stddef.h>
+#include <type_traits>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids
+ * the overhead of malloc when many objects are allocated. It is most useful when creating many
+ * small objects with a similar lifetime, and doesn't add significant overhead for large
+ * allocations.
+ */
+class LinearAllocator {
+public:
+    LinearAllocator();
+    ~LinearAllocator();
+
+    /**
+     * Reserves and returns a region of memory of at least size 'size', aligning as needed.
+     * Typically this is used in an object's overridden new() method or as a replacement for malloc.
+     *
+     * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
+     * delete() on an object stored in a buffer is needed, it should be overridden to use
+     * rewindIfLastAlloc()
+     */
+    void* alloc(size_t size);
+
+    /**
+     * Allocates an instance of the template type with the default constructor
+     * and adds it to the automatic destruction list.
+     */
+    template<class T>
+    T* alloc() {
+        T* ret = new (*this) T;
+        autoDestroy(ret);
+        return ret;
+    }
+
+    /**
+     * Adds the pointer to the tracking list to have its destructor called
+     * when the LinearAllocator is destroyed.
+     */
+    template<class T>
+    void autoDestroy(T* addr) {
+        if (!std::is_trivially_destructible<T>::value) {
+            auto dtor = [](void* addr) { ((T*)addr)->~T(); };
+            addToDestructionList(dtor, addr);
+        }
+    }
+
+    /**
+     * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its
+     * state if possible.
+     */
+    void rewindIfLastAlloc(void* ptr, size_t allocSize);
+
+    /**
+     * Same as rewindIfLastAlloc(void*, size_t)
+     */
+    template<class T>
+    void rewindIfLastAlloc(T* ptr) {
+        rewindIfLastAlloc((void*)ptr, sizeof(T));
+    }
+
+    /**
+     * Dump memory usage statistics to the log (allocated and wasted space)
+     */
+    void dumpMemoryStats(const char* prefix = "");
+
+    /**
+     * The number of bytes used for buffers allocated in the LinearAllocator (does not count space
+     * wasted)
+     */
+    size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
+
+private:
+    LinearAllocator(const LinearAllocator& other);
+
+    class Page;
+    typedef void (*Destructor)(void* addr);
+    struct DestructorNode {
+        Destructor dtor;
+        void* addr;
+        DestructorNode* next = nullptr;
+    };
+
+    void addToDestructionList(Destructor, void* addr);
+    void runDestructorFor(void* addr);
+    Page* newPage(size_t pageSize);
+    bool fitsInCurrentPage(size_t size);
+    void ensureNext(size_t size);
+    void* start(Page *p);
+    void* end(Page* p);
+
+    size_t mPageSize;
+    size_t mMaxAllocSize;
+    void* mNext;
+    Page* mCurrentPage;
+    Page* mPages;
+    DestructorNode* mDtorList = nullptr;
+
+    // Memory usage tracking
+    size_t mTotalAllocated;
+    size_t mWastedSpace;
+    size_t mPageCount;
+    size_t mDedicatedPageCount;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la);
+
+#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index ff6fed2..a7e092f 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -55,16 +55,16 @@
     public static final int ENCODING_DTS_HD = 8;
 
     /** Invalid audio channel configuration */
-    /** @deprecated use CHANNEL_INVALID instead  */
+    /** @deprecated Use {@link #CHANNEL_INVALID} instead.  */
     @Deprecated    public static final int CHANNEL_CONFIGURATION_INVALID   = 0;
     /** Default audio channel configuration */
-    /** @deprecated use CHANNEL_OUT_DEFAULT or CHANNEL_IN_DEFAULT instead  */
+    /** @deprecated Use {@link #CHANNEL_OUT_DEFAULT} or {@link #CHANNEL_IN_DEFAULT} instead.  */
     @Deprecated    public static final int CHANNEL_CONFIGURATION_DEFAULT   = 1;
     /** Mono audio configuration */
-    /** @deprecated use CHANNEL_OUT_MONO or CHANNEL_IN_MONO instead  */
+    /** @deprecated Use {@link #CHANNEL_OUT_MONO} or {@link #CHANNEL_IN_MONO} instead.  */
     @Deprecated    public static final int CHANNEL_CONFIGURATION_MONO      = 2;
     /** Stereo (2 channel) audio configuration */
-    /** @deprecated use CHANNEL_OUT_STEREO or CHANNEL_IN_STEREO instead  */
+    /** @deprecated Use {@link #CHANNEL_OUT_STEREO} or {@link #CHANNEL_IN_STEREO} instead.  */
     @Deprecated    public static final int CHANNEL_CONFIGURATION_STEREO    = 3;
 
     /** Invalid audio channel mask */
@@ -117,12 +117,11 @@
     public static final int CHANNEL_OUT_5POINT1_SIDE = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
             CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY |
             CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT);
-    // TODO does this need an @deprecated ?
-    // different from AUDIO_CHANNEL_OUT_7POINT1
-    public static final int CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+    // different from AUDIO_CHANNEL_OUT_7POINT1 used internally, and not accepted by AudioRecord.
+    /** @deprecated Not the typical 7.1 surround configuration. Use {@link #CHANNEL_OUT_7POINT1_SURROUND} instead. */
+    @Deprecated    public static final int CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
             CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
             CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER);
-    /** @hide */
     // matches AUDIO_CHANNEL_OUT_7POINT1
     public static final int CHANNEL_OUT_7POINT1_SURROUND = (
             CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index cb70e8b..d851ad7 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3147,6 +3147,20 @@
             "android.media.property.OUTPUT_FRAMES_PER_BUFFER";
 
     /**
+     * Used as a key for {@link #getProperty} to determine if the default microphone audio source
+     * supports near-ultrasound frequencies (range of 18 - 21 kHz).
+     */
+    public static final String PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND =
+            "android.media.property.SUPPORT_MIC_NEAR_ULTRASOUND";
+
+    /**
+     * Used as a key for {@link #getProperty} to determine if the default speaker audio path
+     * supports near-ultrasound frequencies (range of 18 - 21 kHz).
+     */
+    public static final String PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND =
+            "android.media.property.SUPPORT_SPEAKER_NEAR_ULTRASOUND";
+
+    /**
      * Returns the value of the property with the specified key.
      * @param key One of the strings corresponding to a property key: either
      *            {@link #PROPERTY_OUTPUT_SAMPLE_RATE} or
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 2346abe..201a796 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -59,7 +59,7 @@
     /** Minimum value for sample rate */
     private static final int SAMPLE_RATE_HZ_MIN = 4000;
     /** Maximum value for sample rate */
-    private static final int SAMPLE_RATE_HZ_MAX = 96000;
+    private static final int SAMPLE_RATE_HZ_MAX = 192000;
 
     /**
      *  indicates AudioRecord state is not successfully initialized.
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 847bdc2..9bd5c55 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -96,7 +96,7 @@
     /** Minimum value for sample rate */
     private static final int SAMPLE_RATE_HZ_MIN = 4000;
     /** Maximum value for sample rate */
-    private static final int SAMPLE_RATE_HZ_MAX = 96000;
+    private static final int SAMPLE_RATE_HZ_MAX = 192000;
 
     /** Maximum value for AudioTrack channel count */
     private static final int CHANNEL_COUNT_MAX = 8;
@@ -491,7 +491,7 @@
         session[0] = sessionId;
         // native initialization
         int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
-                mSampleRate, mChannels, mAudioFormat,
+                mSampleRate, mChannels, mChannelIndexMask, mAudioFormat,
                 mNativeBufferSizeInBytes, mDataLoadMode, session);
         if (initResult != SUCCESS) {
             loge("Error code "+initResult+" when initializing AudioTrack.");
@@ -701,48 +701,6 @@
             AudioFormat.CHANNEL_OUT_SIDE_LEFT |
             AudioFormat.CHANNEL_OUT_SIDE_RIGHT;
 
-    // Java channel mask definitions below match those
-    // in /system/core/include/system/audio.h in the JNI code of AudioTrack.
-
-    // internal maximum size for bits parameter, not part of public API
-    private static final int AUDIO_CHANNEL_BITS_LOG2 = 30;
-
-    // log(2) of maximum number of representations, not part of public API
-    private static final int AUDIO_CHANNEL_REPRESENTATION_LOG2 = 2;
-
-    // used to create a channel index mask or channel position mask
-    // with getChannelMaskFromRepresentationAndBits();
-    private static final int CHANNEL_OUT_REPRESENTATION_POSITION = 0;
-    private static final int CHANNEL_OUT_REPRESENTATION_INDEX = 2;
-
-    /**
-     * Return the channel mask from its representation and bits.
-     *
-     * This creates a channel mask for mChannels which combines a
-     * representation field and a bits field.  This is for internal
-     * communication to native code, not part of the public API.
-     *
-     * @param representation the type of channel mask,
-     *   either CHANNEL_OUT_REPRESENTATION_POSITION
-     *   or CHANNEL_OUT_REPRESENTATION_INDEX
-     * @param bits is the channel bits specifying occupancy
-     * @return the channel mask
-     * @throws java.lang.IllegalArgumentException if representation is not recognized or
-     *   the bits field is not acceptable for that representation
-     */
-    private static int getChannelMaskFromRepresentationAndBits(int representation, int bits) {
-        switch (representation) {
-        case CHANNEL_OUT_REPRESENTATION_POSITION:
-        case CHANNEL_OUT_REPRESENTATION_INDEX:
-            if ((bits & ~((1 << AUDIO_CHANNEL_BITS_LOG2) - 1)) != 0) {
-                throw new IllegalArgumentException("invalid bits " + bits);
-            }
-            return representation << AUDIO_CHANNEL_BITS_LOG2 | bits;
-        default:
-            throw new IllegalArgumentException("invalid representation " + representation);
-        }
-    }
-
     // Convenience method for the constructor's parameter checks.
     // This is where constructor IllegalArgumentException-s are thrown
     // postconditions:
@@ -804,11 +762,6 @@
             } else if (mChannelCount != channelIndexCount) {
                 throw new IllegalArgumentException("Channel count must match");
             }
-
-            // AudioTrack prefers to use the channel index configuration
-            // over the channel position configuration if both are specified.
-            mChannels = getChannelMaskFromRepresentationAndBits(
-                    CHANNEL_OUT_REPRESENTATION_INDEX, mChannelIndexMask);
         }
 
         //--------------
@@ -1448,7 +1401,8 @@
      * the position values have different meanings.
      * <br>
      * If looping is currently enabled and the new position is greater than or equal to the
-     * loop end marker, the behavior varies by API level: for API level 22 and above,
+     * loop end marker, the behavior varies by API level:
+     * as of {@link android.os.Build.VERSION_CODES#MNC},
      * the looping is first disabled and then the position is set.
      * For earlier API levels, the behavior is unspecified.
      * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
@@ -1485,7 +1439,7 @@
      * {@link #ERROR_BAD_VALUE} is returned.
      * The loop range is the interval [startInFrames, endInFrames).
      * <br>
-     * For API level 22 and above, the position is left unchanged,
+     * As of {@link android.os.Build.VERSION_CODES#MNC}, the position is left unchanged,
      * unless it is greater than or equal to the loop end marker, in which case
      * it is forced to the loop start marker.
      * For earlier API levels, the effect on position is unspecified.
@@ -2032,12 +1986,12 @@
      * The track must be stopped or paused, and
      * the track's creation mode must be {@link #MODE_STATIC}.
      * <p>
-     * For API level 22 and above, also resets the value returned by
+     * As of {@link android.os.Build.VERSION_CODES#MNC}, also resets the value returned by
      * {@link #getPlaybackHeadPosition()} to zero.
      * For earlier API levels, the reset behavior is unspecified.
      * <p>
-     * {@link #setPlaybackHeadPosition(int)} to zero
-     * is recommended instead when the reset of {@link #getPlaybackHeadPosition} is not needed.
+     * Use {@link #setPlaybackHeadPosition(int)} with a zero position
+     * if the reset of <code>getPlaybackHeadPosition()</code> is not needed.
      * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
      *  {@link #ERROR_INVALID_OPERATION}
      */
@@ -2361,7 +2315,7 @@
     //     AudioAttributes.USAGE_MEDIA will map to AudioManager.STREAM_MUSIC
     private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
             Object /*AudioAttributes*/ attributes,
-            int sampleRate, int channelMask, int audioFormat,
+            int sampleRate, int channelMask, int channelIndexMask, int audioFormat,
             int buffSizeInBytes, int mode, int[] sessionId);
 
     private native final void native_finalize();
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 75901fd..195c987 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -50,10 +50,25 @@
     /**
      * @hide
      */
+    protected boolean mIsImageValid = false;
+
+    /**
+     * @hide
+     */
     protected Image() {
     }
 
     /**
+     * Throw IllegalStateException if the image is invalid (already closed).
+     *
+     * @hide
+     */
+    protected void throwISEIfImageIsInvalid() {
+        if (!mIsImageValid) {
+            throw new IllegalStateException("Image is already closed");
+        }
+    }
+    /**
      * Get the format for this image. This format determines the number of
      * ByteBuffers needed to represent the image, and the general layout of the
      * pixel data in each in ByteBuffer.
@@ -160,7 +175,7 @@
      * Set the timestamp associated with this frame.
      * <p>
      * The timestamp is measured in nanoseconds, and is normally monotonically
-     * increasing. However, However, the behavior of the timestamp depends on
+     * increasing. However, the behavior of the timestamp depends on
      * the destination of this image. See {@link android.hardware.Camera Camera}
      * , {@link android.hardware.camera2.CameraDevice CameraDevice},
      * {@link MediaPlayer} and {@link MediaCodec} for more details.
@@ -176,6 +191,7 @@
      * @param timestamp The timestamp to be set for this image.
      */
     public void setTimestamp(long timestamp) {
+        throwISEIfImageIsInvalid();
         return;
     }
 
@@ -187,6 +203,7 @@
      * </p>
      */
     public boolean isOpaque() {
+        throwISEIfImageIsInvalid();
         return false;
     }
 
@@ -199,6 +216,8 @@
      * using coordinates in the largest-resolution plane.
      */
     public Rect getCropRect() {
+        throwISEIfImageIsInvalid();
+
         if (mCropRect == null) {
             return new Rect(0, 0, getWidth(), getHeight());
         } else {
@@ -213,6 +232,8 @@
      * using coordinates in the largest-resolution plane.
      */
     public void setCropRect(Rect cropRect) {
+        throwISEIfImageIsInvalid();
+
         if (cropRect != null) {
             cropRect = new Rect(cropRect);  // make a copy
             cropRect.intersect(0, 0, getWidth(), getHeight());
@@ -260,6 +281,8 @@
      *         a new owner.
      */
     boolean isAttachable() {
+        throwISEIfImageIsInvalid();
+
         return false;
     }
 
@@ -279,6 +302,8 @@
      * @return The owner of the Image.
      */
     Object getOwner() {
+        throwISEIfImageIsInvalid();
+
         return null;
     }
 
@@ -294,6 +319,8 @@
      * @return native context associated with this Image.
      */
     long getNativeContext() {
+        throwISEIfImageIsInvalid();
+
         return 0;
     }
 
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index e54525d..6d30208 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -368,7 +368,7 @@
         switch (status) {
             case ACQUIRE_SUCCESS:
                 si.createSurfacePlanes();
-                si.setImageValid(true);
+                si.mIsImageValid = true;
             case ACQUIRE_NO_BUFS:
             case ACQUIRE_MAX_IMAGES:
                 break;
@@ -444,7 +444,7 @@
 
         si.clearSurfacePlanes();
         nativeReleaseImage(i);
-        si.setImageValid(false);
+        si.mIsImageValid = false;
     }
 
     /**
@@ -686,7 +686,6 @@
 
     private class SurfaceImage extends android.media.Image {
         public SurfaceImage(int format) {
-            mIsImageValid = false;
             mFormat = format;
         }
 
@@ -784,16 +783,6 @@
             mIsDetached.getAndSet(detached);
         }
 
-        private void setImageValid(boolean isValid) {
-            mIsImageValid = isValid;
-        }
-
-        private void throwISEIfImageIsInvalid() {
-            if (!mIsImageValid) {
-                throw new IllegalStateException("Image is already closed");
-            }
-        }
-
         private void clearSurfacePlanes() {
             if (mIsImageValid) {
                 for (int i = 0; i < mPlanes.length; i++) {
@@ -877,7 +866,6 @@
         private long mTimestamp;
 
         private SurfacePlane[] mPlanes;
-        private boolean mIsImageValid;
         private int mHeight = -1;
         private int mWidth = -1;
         private int mFormat = ImageFormat.UNKNOWN;
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index c18b463..f805339 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -29,7 +29,6 @@
 import java.nio.NioUtils;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * <p>
@@ -204,7 +203,7 @@
         WriterSurfaceImage newImage = new WriterSurfaceImage(this);
         nativeDequeueInputImage(mNativeContext, newImage);
         mDequeuedImages.add(newImage);
-        newImage.setImageValid(true);
+        newImage.mIsImageValid = true;
         return newImage;
     }
 
@@ -260,7 +259,7 @@
             throw new IllegalArgumentException("image shouldn't be null");
         }
         boolean ownedByMe = isImageOwnedByMe(image);
-        if (ownedByMe && !(((WriterSurfaceImage) image).isImageValid())) {
+        if (ownedByMe && !(((WriterSurfaceImage) image).mIsImageValid)) {
             throw new IllegalStateException("Image from ImageWriter is invalid");
         }
 
@@ -312,7 +311,7 @@
             // Do not call close here, as close is essentially cancel image.
             WriterSurfaceImage wi = (WriterSurfaceImage) image;
             wi.clearSurfacePlanes();
-            wi.setImageValid(false);
+            wi.mIsImageValid = false;
         }
     }
 
@@ -555,7 +554,7 @@
 
         WriterSurfaceImage wi = (WriterSurfaceImage) image;
 
-        if (!wi.isImageValid()) {
+        if (!wi.mIsImageValid) {
             throw new IllegalStateException("Image is invalid");
         }
 
@@ -568,7 +567,7 @@
         cancelImage(mNativeContext, image);
         mDequeuedImages.remove(image);
         wi.clearSurfacePlanes();
-        wi.setImageValid(false);
+        wi.mIsImageValid = false;
     }
 
     private boolean isImageOwnedByMe(Image image) {
@@ -585,7 +584,6 @@
 
     private static class WriterSurfaceImage extends android.media.Image {
         private ImageWriter mOwner;
-        private AtomicBoolean mIsImageValid = new AtomicBoolean(false);
         // This field is used by native code, do not access or modify.
         private long mNativeBuffer;
         private int mNativeFenceFd = -1;
@@ -604,9 +602,8 @@
 
         @Override
         public int getFormat() {
-            if (!mIsImageValid.get()) {
-                throw new IllegalStateException("Image is already released");
-            }
+            throwISEIfImageIsInvalid();
+
             if (mFormat == -1) {
                 mFormat = nativeGetFormat();
             }
@@ -615,9 +612,7 @@
 
         @Override
         public int getWidth() {
-            if (!mIsImageValid.get()) {
-                throw new IllegalStateException("Image is already released");
-            }
+            throwISEIfImageIsInvalid();
 
             if (mWidth == -1) {
                 mWidth = nativeGetWidth();
@@ -628,9 +623,7 @@
 
         @Override
         public int getHeight() {
-            if (!mIsImageValid.get()) {
-                throw new IllegalStateException("Image is already released");
-            }
+            throwISEIfImageIsInvalid();
 
             if (mHeight == -1) {
                 mHeight = nativeGetHeight();
@@ -641,36 +634,28 @@
 
         @Override
         public long getTimestamp() {
-            if (!mIsImageValid.get()) {
-                throw new IllegalStateException("Image is already released");
-            }
+            throwISEIfImageIsInvalid();
 
             return mTimestamp;
         }
 
         @Override
         public void setTimestamp(long timestamp) {
-            if (!mIsImageValid.get()) {
-                throw new IllegalStateException("Image is already released");
-            }
+            throwISEIfImageIsInvalid();
 
             mTimestamp = timestamp;
         }
 
         @Override
         public boolean isOpaque() {
-            if (!mIsImageValid.get()) {
-                throw new IllegalStateException("Image is already released");
-            }
+            throwISEIfImageIsInvalid();
 
             return getFormat() == ImageFormat.PRIVATE;
         }
 
         @Override
         public Plane[] getPlanes() {
-            if (!mIsImageValid.get()) {
-                throw new IllegalStateException("Image is already released");
-            }
+            throwISEIfImageIsInvalid();
 
             if (mPlanes == null) {
                 int numPlanes = ImageUtils.getNumPlanesForFormat(getFormat());
@@ -682,9 +667,7 @@
 
         @Override
         boolean isAttachable() {
-            if (!mIsImageValid.get()) {
-                throw new IllegalStateException("Image is already released");
-            }
+            throwISEIfImageIsInvalid();
             // Don't allow Image to be detached from ImageWriter for now, as no
             // detach API is exposed.
             return false;
@@ -692,17 +675,21 @@
 
         @Override
         ImageWriter getOwner() {
+            throwISEIfImageIsInvalid();
+
             return mOwner;
         }
 
         @Override
         long getNativeContext() {
+            throwISEIfImageIsInvalid();
+
             return mNativeBuffer;
         }
 
         @Override
         public void close() {
-            if (mIsImageValid.get()) {
+            if (mIsImageValid) {
                 getOwner().abortImage(this);
             }
         }
@@ -716,16 +703,8 @@
             }
         }
 
-        private boolean isImageValid() {
-            return mIsImageValid.get();
-        }
-
-        private void setImageValid(boolean isValid) {
-            mIsImageValid.getAndSet(isValid);
-        }
-
         private void clearSurfacePlanes() {
-            if (mIsImageValid.get()) {
+            if (mIsImageValid) {
                 for (int i = 0; i < mPlanes.length; i++) {
                     if (mPlanes[i] != null) {
                         mPlanes[i].clearBuffer();
@@ -756,26 +735,19 @@
 
             @Override
             public int getRowStride() {
-                if (WriterSurfaceImage.this.isImageValid() == false) {
-                    throw new IllegalStateException("Image is already released");
-                }
+                throwISEIfImageIsInvalid();
                 return mRowStride;
             }
 
             @Override
             public int getPixelStride() {
-                if (WriterSurfaceImage.this.isImageValid() == false) {
-                    throw new IllegalStateException("Image is already released");
-                }
+                throwISEIfImageIsInvalid();
                 return mPixelStride;
             }
 
             @Override
             public ByteBuffer getBuffer() {
-                if (WriterSurfaceImage.this.isImageValid() == false) {
-                    throw new IllegalStateException("Image is already released");
-                }
-
+                throwISEIfImageIsInvalid();
                 return mBuffer;
             }
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 1f00c7b..d22cfda 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2059,7 +2059,6 @@
     /** @hide */
     public static class MediaImage extends Image {
         private final boolean mIsReadOnly;
-        private boolean mIsValid;
         private final int mWidth;
         private final int mHeight;
         private final int mFormat;
@@ -2072,36 +2071,42 @@
 
         private final static int TYPE_YUV = 1;
 
+        @Override
         public int getFormat() {
-            checkValid();
+            throwISEIfImageIsInvalid();
             return mFormat;
         }
 
+        @Override
         public int getHeight() {
-            checkValid();
+            throwISEIfImageIsInvalid();
             return mHeight;
         }
 
+        @Override
         public int getWidth() {
-            checkValid();
+            throwISEIfImageIsInvalid();
             return mWidth;
         }
 
+        @Override
         public long getTimestamp() {
-            checkValid();
+            throwISEIfImageIsInvalid();
             return mTimestamp;
         }
 
+        @Override
         @NonNull
         public Plane[] getPlanes() {
-            checkValid();
+            throwISEIfImageIsInvalid();
             return Arrays.copyOf(mPlanes, mPlanes.length);
         }
 
+        @Override
         public void close() {
-            if (mIsValid) {
+            if (mIsImageValid) {
                 java.nio.NioUtils.freeDirectBuffer(mBuffer);
-                mIsValid = false;
+                mIsImageValid = false;
             }
         }
 
@@ -2111,6 +2116,7 @@
          * The crop rectangle specifies the region of valid pixels in the image,
          * using coordinates in the largest-resolution plane.
          */
+        @Override
         public void setCropRect(@Nullable Rect cropRect) {
             if (mIsReadOnly) {
                 throw new ReadOnlyBufferException();
@@ -2118,11 +2124,6 @@
             super.setCropRect(cropRect);
         }
 
-        private void checkValid() {
-            if (!mIsValid) {
-                throw new IllegalStateException("Image is already released");
-            }
-        }
 
         private int readInt(@NonNull ByteBuffer buffer, boolean asLong) {
             if (asLong) {
@@ -2137,7 +2138,7 @@
                 long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
             mFormat = ImageFormat.YUV_420_888;
             mTimestamp = timestamp;
-            mIsValid = true;
+            mIsImageValid = true;
             mIsReadOnly = buffer.isReadOnly();
             mBuffer = buffer.duplicate();
 
@@ -2208,20 +2209,20 @@
 
             @Override
             public int getRowStride() {
-                checkValid();
+                throwISEIfImageIsInvalid();
                 return mRowInc;
             }
 
             @Override
             public int getPixelStride() {
-                checkValid();
+                throwISEIfImageIsInvalid();
                 return mColInc;
             }
 
             @Override
             @NonNull
             public ByteBuffer getBuffer() {
-                checkValid();
+                throwISEIfImageIsInvalid();
                 return mData;
             }
 
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index 3b4f8e5..dc6760d 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -49,7 +49,7 @@
  * sync.setAudioTrack(audioTrack);
  * sync.setCallback(new MediaSync.Callback() {
  *     {@literal @Override}
- *     public void onReturnAudioBuffer(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
+ *     public void onAudioBufferConsumed(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
  *         ...
  *     }
  * }, null);
@@ -88,7 +88,7 @@
  * }
  *
  * // This is the callback from MediaSync.
- * onReturnAudioBuffer(MediaSync sync, ByteBuffer buffer, int bufferIndex) {
+ * onAudioBufferConsumed(MediaSync sync, ByteBuffer buffer, int bufferIndex) {
  *     // ...
  *     audioDecoder.releaseBuffer(bufferIndex, false);
  *     // ...
@@ -104,7 +104,7 @@
  * <p>
  * For audio, the client needs to set up audio track correctly, e.g., using {@link
  * AudioTrack#MODE_STREAM}. The audio buffers are sent to MediaSync directly via {@link
- * #queueAudio}, and are returned to the client via {@link Callback#onReturnAudioBuffer}
+ * #queueAudio}, and are returned to the client via {@link Callback#onAudioBufferConsumed}
  * asynchronously. The client should not modify an audio buffer till it's returned.
  * <p>
  * The client can optionally pre-fill audio/video buffers by setting playback rate to 0.0,
@@ -125,10 +125,41 @@
          * @param audioBuffer The returned audio buffer.
          * @param bufferIndex The index associated with the audio buffer
          */
-        public abstract void onReturnAudioBuffer(
+        public abstract void onAudioBufferConsumed(
                 @NonNull MediaSync sync, @NonNull ByteBuffer audioBuffer, int bufferIndex);
     }
 
+    /** Audio track failed.
+     * @see android.media.MediaSync.OnErrorListener
+     */
+    public static final int MEDIASYNC_ERROR_AUDIOTRACK_FAIL = 1;
+
+    /** The surface failed to handle video buffers.
+     * @see android.media.MediaSync.OnErrorListener
+     */
+    public static final int MEDIASYNC_ERROR_SURFACE_FAIL = 2;
+
+    /**
+     * Interface definition of a callback to be invoked when there
+     * has been an error during an asynchronous operation (other errors
+     * will throw exceptions at method call time).
+     */
+    public interface OnErrorListener {
+        /**
+         * Called to indicate an error.
+         *
+         * @param sync The MediaSync the error pertains to
+         * @param what The type of error that has occurred:
+         * <ul>
+         * <li>{@link #MEDIASYNC_ERROR_AUDIOTRACK_FAIL}
+         * <li>{@link #MEDIASYNC_ERROR_SURFACE_FAIL}
+         * </ul>
+         * @param extra an extra code, specific to the error. Typically
+         * implementation dependent.
+         */
+        void onError(@NonNull MediaSync sync, int what, int extra);
+    }
+
     private static final String TAG = "MediaSync";
 
     private static final int EVENT_CALLBACK = 1;
@@ -155,6 +186,10 @@
     private Handler mCallbackHandler = null;
     private MediaSync.Callback mCallback = null;
 
+    private final Object mOnErrorListenerLock = new Object();
+    private Handler mOnErrorListenerHandler = null;
+    private MediaSync.OnErrorListener mOnErrorListener = null;
+
     private Thread mAudioThread = null;
     // Created on mAudioThread when mAudioThread is started. When used on user thread, they should
     // be guarded by checking mAudioThread.
@@ -235,6 +270,39 @@
     }
 
     /**
+     * Sets an asynchronous callback for error events.
+     * <p>
+     * This method can be called multiple times to update a previously set listener. If the
+     * handler is changed, undelivered notifications scheduled for the old handler may be dropped.
+     * <p>
+     * <b>Do not call this inside callback.</b>
+     *
+     * @param listener The callback that will run. Use {@code null} to stop receiving callbacks.
+     * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's
+     *     internal handler if it exists.
+     */
+    public void setOnErrorListener(@Nullable /* MediaSync. */ OnErrorListener listener,
+            @Nullable Handler handler) {
+        synchronized(mOnErrorListenerLock) {
+            if (handler != null) {
+                mOnErrorListenerHandler = handler;
+            } else {
+                Looper looper;
+                if ((looper = Looper.myLooper()) == null) {
+                    looper = Looper.getMainLooper();
+                }
+                if (looper == null) {
+                    mOnErrorListenerHandler = null;
+                } else {
+                    mOnErrorListenerHandler = new Handler(looper);
+                }
+            }
+
+            mOnErrorListener = listener;
+        }
+    }
+
+    /**
      * Sets the output surface for MediaSync.
      * <p>
      * Currently, this is only supported in the Initialized state.
@@ -614,7 +682,7 @@
                                 return;
                             }
                             if (mCallback != null) {
-                                mCallback.onReturnAudioBuffer(sync, audioBuffer.mByteBuffer,
+                                mCallback.onAudioBufferConsumed(sync, audioBuffer.mByteBuffer,
                                         audioBuffer.mBufferIndex);
                             }
                         }
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 754facd..966e41a 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -16,9 +16,12 @@
 
 package android.media.tv;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.text.TextUtils;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -693,6 +696,12 @@
     private final int mHashCode;
 
     /**
+     * Rating constant denoting unrated content.
+     */
+    public static final TvContentRating UNRATED = new TvContentRating("com.android.tv", "",
+            "UNRATED", null);
+
+    /**
      * Creates a {@code TvContentRating} object with predefined content rating strings.
      *
      * @param domain The domain string. For example, "com.android.tv".
@@ -833,10 +842,8 @@
      * @hide
      */
     @SystemApi
-    public final boolean contains(TvContentRating rating) {
-        if (rating == null) {
-            throw new IllegalArgumentException("rating cannot be null");
-        }
+    public final boolean contains(@NonNull TvContentRating rating) {
+        Preconditions.checkNotNull(rating);
         if (!rating.getMainRating().equals(mRating)) {
             return false;
         }
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index f0e2f5c5..46d33b4 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
@@ -424,7 +425,7 @@
      * @return a CharSequence containing the TV input's label. If the TV input does not have
      *         a label, its name is returned.
      */
-    public CharSequence loadLabel(Context context) {
+    public CharSequence loadLabel(@NonNull Context context) {
         if (TextUtils.isEmpty(mLabel)) {
             return mService.loadLabel(context.getPackageManager());
         } else {
@@ -452,7 +453,7 @@
      * @return a Drawable containing the TV input's icon. If the TV input does not have an icon,
      *         application's icon is returned. If it's unavailable too, {@code null} is returned.
      */
-    public Drawable loadIcon(Context context) {
+    public Drawable loadIcon(@NonNull Context context) {
         if (mIconUri == null) {
             return loadServiceIcon(context);
         }
@@ -506,7 +507,7 @@
      * @param flags The flags used for parceling.
      */
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mId);
         dest.writeString(mParentId);
         mService.writeToParcel(dest, flags);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 105084e..601fa45 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.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.graphics.Rect;
@@ -39,6 +40,8 @@
 import android.view.Surface;
 import android.view.View;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -932,10 +935,8 @@
      * @return the {@link TvInputInfo} for a given TV input. {@code null} if not found.
      */
     @Nullable
-    public TvInputInfo getTvInputInfo(String inputId) {
-        if (inputId == null) {
-            throw new IllegalArgumentException("inputId cannot be null");
-        }
+    public TvInputInfo getTvInputInfo(@NonNull String inputId) {
+        Preconditions.checkNotNull(inputId);
         try {
             return mService.getTvInputInfo(inputId, mUserId);
         } catch (RemoteException e) {
@@ -956,10 +957,8 @@
      * @param inputId The id of the TV input.
      * @throws IllegalArgumentException if the argument is {@code null}.
      */
-    public int getInputState(String inputId) {
-        if (inputId == null) {
-            throw new IllegalArgumentException("inputId cannot be null");
-        }
+    public int getInputState(@NonNull String inputId) {
+        Preconditions.checkNotNull(inputId);
         synchronized (mLock) {
             Integer state = mStateMap.get(inputId);
             if (state == null) {
@@ -975,15 +974,10 @@
      *
      * @param callback A callback used to monitor status of the TV inputs.
      * @param handler A {@link Handler} that the status change will be delivered to.
-     * @throws IllegalArgumentException if any of the arguments is {@code null}.
      */
-    public void registerCallback(TvInputCallback callback, Handler handler) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-        if (handler == null) {
-            throw new IllegalArgumentException("handler cannot be null");
-        }
+    public void registerCallback(@NonNull TvInputCallback callback, @NonNull Handler handler) {
+        Preconditions.checkNotNull(callback);
+        Preconditions.checkNotNull(handler);
         synchronized (mLock) {
             mCallbackRecords.add(new TvInputCallbackRecord(callback, handler));
         }
@@ -993,12 +987,9 @@
      * Unregisters the existing {@link TvInputCallback}.
      *
      * @param callback The existing callback to remove.
-     * @throws IllegalArgumentException if any of the arguments is {@code null}.
      */
-    public void unregisterCallback(final TvInputCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
+    public void unregisterCallback(@NonNull final TvInputCallback callback) {
+        Preconditions.checkNotNull(callback);
         synchronized (mLock) {
             for (Iterator<TvInputCallbackRecord> it = mCallbackRecords.iterator();
                     it.hasNext(); ) {
@@ -1047,10 +1038,8 @@
      * @param rating The TV content rating to check.
      * @return {@code true} if the given TV content rating is blocked, {@code false} otherwise.
      */
-    public boolean isRatingBlocked(TvContentRating rating) {
-        if (rating == null) {
-            throw new IllegalArgumentException("rating cannot be null");
-        }
+    public boolean isRatingBlocked(@NonNull TvContentRating rating) {
+        Preconditions.checkNotNull(rating);
         try {
             return mService.isRatingBlocked(rating.flattenToString(), mUserId);
         } catch (RemoteException e) {
@@ -1086,10 +1075,8 @@
      * @hide
      */
     @SystemApi
-    public void addBlockedRating(TvContentRating rating) {
-        if (rating == null) {
-            throw new IllegalArgumentException("rating cannot be null");
-        }
+    public void addBlockedRating(@NonNull TvContentRating rating) {
+        Preconditions.checkNotNull(rating);
         try {
             mService.addBlockedRating(rating.flattenToString(), mUserId);
         } catch (RemoteException e) {
@@ -1106,10 +1093,8 @@
      * @hide
      */
     @SystemApi
-    public void removeBlockedRating(TvContentRating rating) {
-        if (rating == null) {
-            throw new IllegalArgumentException("rating cannot be null");
-        }
+    public void removeBlockedRating(@NonNull TvContentRating rating) {
+        Preconditions.checkNotNull(rating);
         try {
             mService.removeBlockedRating(rating.flattenToString(), mUserId);
         } catch (RemoteException e) {
@@ -1139,21 +1124,14 @@
      * @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.
-     * @throws IllegalArgumentException if any of the arguments is {@code null}.
      * @hide
      */
     @SystemApi
-    public void createSession(String inputId, final SessionCallback callback,
-            Handler handler) {
-        if (inputId == null) {
-            throw new IllegalArgumentException("id cannot be null");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-        if (handler == null) {
-            throw new IllegalArgumentException("handler cannot be null");
-        }
+    public void createSession(@NonNull String inputId, @NonNull final SessionCallback callback,
+            @NonNull Handler handler) {
+        Preconditions.checkNotNull(inputId);
+        Preconditions.checkNotNull(callback);
+        Preconditions.checkNotNull(handler);
         SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
         synchronized (mSessionCallbackRecordMap) {
             int seq = mNextSeq++;
@@ -1435,7 +1413,6 @@
          * Tunes to a given channel.
          *
          * @param channelUri The URI of a channel.
-         * @throws IllegalArgumentException if the argument is {@code null}.
          */
         public void tune(Uri channelUri) {
             tune(channelUri, null);
@@ -1446,14 +1423,11 @@
          *
          * @param channelUri The URI of a channel.
          * @param params A set of extra parameters which might be handled with this tune event.
-         * @throws IllegalArgumentException if {@code channelUri} is {@code null}.
          * @hide
          */
         @SystemApi
-        public void tune(Uri channelUri, Bundle params) {
-            if (channelUri == null) {
-                throw new IllegalArgumentException("channelUri cannot be null");
-            }
+        public void tune(@NonNull Uri channelUri, Bundle params) {
+            Preconditions.checkNotNull(channelUri);
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
@@ -1789,16 +1763,11 @@
          *
          * @param view A view playing TV.
          * @param frame A position of the overlay view.
-         * @throws IllegalArgumentException if any of the arguments is {@code null}.
          * @throws IllegalStateException if {@code view} is not attached to a window.
          */
-        void createOverlayView(View view, Rect frame) {
-            if (view == null) {
-                throw new IllegalArgumentException("view cannot be null");
-            }
-            if (frame == null) {
-                throw new IllegalArgumentException("frame cannot be null");
-            }
+        void createOverlayView(@NonNull View view, @NonNull Rect frame) {
+            Preconditions.checkNotNull(view);
+            Preconditions.checkNotNull(frame);
             if (view.getWindowToken() == null) {
                 throw new IllegalStateException("view must be attached to a window");
             }
@@ -1817,12 +1786,9 @@
          * Relayouts the current overlay view.
          *
          * @param frame A new position of the overlay view.
-         * @throws IllegalArgumentException if the arguments is {@code null}.
          */
-        void relayoutOverlayView(Rect frame) {
-            if (frame == null) {
-                throw new IllegalArgumentException("frame cannot be null");
-            }
+        void relayoutOverlayView(@NonNull Rect frame) {
+            Preconditions.checkNotNull(frame);
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
@@ -1852,14 +1818,12 @@
         /**
          * Requests to unblock content blocked by parental controls.
          */
-        void requestUnblockContent(TvContentRating unblockedRating) {
+        void requestUnblockContent(@NonNull TvContentRating unblockedRating) {
+            Preconditions.checkNotNull(unblockedRating);
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
-            if (unblockedRating == null) {
-                throw new IllegalArgumentException("unblockedRating cannot be null");
-            }
             try {
                 mService.requestUnblockContent(mToken, unblockedRating.flattenToString(), mUserId);
             } catch (RemoteException e) {
@@ -1870,25 +1834,22 @@
         /**
          * Dispatches an input event to this session.
          *
-         * @param event An {@link InputEvent} to dispatch.
+         * @param event An {@link InputEvent} to dispatch. Cannot be {@code null}.
          * @param token A token used to identify the input event later in the callback.
-         * @param callback A callback used to receive the dispatch result.
-         * @param handler A {@link Handler} that the dispatch result will be delivered to.
+         * @param callback A callback used to receive the dispatch result. Cannot be {@code null}.
+         * @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be
+         *            {@code null}.
          * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
          *         {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
          *         {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
          *         be invoked later.
-         * @throws IllegalArgumentException if any of the necessary arguments is {@code null}.
          * @hide
          */
-        public int dispatchInputEvent(InputEvent event, Object token,
-                FinishedInputEventCallback callback, Handler handler) {
-            if (event == null) {
-                throw new IllegalArgumentException("event cannot be null");
-            }
-            if (callback != null && handler == null) {
-                throw new IllegalArgumentException("handler cannot be null");
-            }
+        public int dispatchInputEvent(@NonNull InputEvent event, Object token,
+                @NonNull FinishedInputEventCallback callback, @NonNull Handler handler) {
+            Preconditions.checkNotNull(event);
+            Preconditions.checkNotNull(callback);
+            Preconditions.checkNotNull(handler);
             synchronized (mHandler) {
                 if (mChannel == null) {
                     return DISPATCH_NOT_HANDLED;
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 382ec91..5156ae8 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.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -50,6 +51,7 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -312,10 +314,8 @@
          * @hide
          */
         @SystemApi
-        public void notifySessionEvent(final String eventType, final Bundle eventArgs) {
-            if (eventType == null) {
-                throw new IllegalArgumentException("eventType should not be null.");
-            }
+        public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) {
+            Preconditions.checkNotNull(eventType);
             executeOrPostRunnable(new Runnable() {
                 @Override
                 public void run() {
@@ -544,7 +544,8 @@
          * @see #notifyContentAllowed
          * @see TvInputManager
          */
-        public void notifyContentBlocked(final TvContentRating rating) {
+        public void notifyContentBlocked(@NonNull final TvContentRating rating) {
+            Preconditions.checkNotNull(rating);
             executeOrPostRunnable(new Runnable() {
                 @Override
                 public void run() {
@@ -828,7 +829,7 @@
          * @hide
          */
         @SystemApi
-        public void onAppPrivateCommand(String action, Bundle data) {
+        public void onAppPrivateCommand(@NonNull String action, Bundle data) {
         }
 
         /**
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 6eedeb4..2c956e9 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -16,10 +16,13 @@
 
 package android.media.tv;
 
+import android.annotation.NonNull;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * Encapsulates the format of tracks played in {@link TvInputService}.
  */
@@ -245,15 +248,13 @@
          * @param id The ID of the track that uniquely identifies the current track among all the
          *            other tracks in the same TV program.
          */
-        public Builder(int type, String id) {
+        public Builder(int type, @NonNull String id) {
             if (type != TYPE_AUDIO
                     && type != TYPE_VIDEO
                     && type != TYPE_SUBTITLE) {
                 throw new IllegalArgumentException("Unknown type: " + type);
             }
-            if (id == null) {
-                throw new IllegalArgumentException("id cannot be null");
-            }
+            Preconditions.checkNotNull(id);
             mType = type;
             mId = id;
         }
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6456049..d248b12 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -273,7 +274,7 @@
      * @param inputId The ID of TV input which will play the given channel.
      * @param channelUri The URI of a channel.
      */
-    public void tune(String inputId, Uri channelUri) {
+    public void tune(@NonNull String inputId, Uri channelUri) {
         tune(inputId, channelUri, null);
     }
 
@@ -494,7 +495,7 @@
      * @hide
      */
     @SystemApi
-    public void sendAppPrivateCommand(String action, Bundle data) {
+    public void sendAppPrivateCommand(@NonNull String action, Bundle data) {
         if (TextUtils.isEmpty(action)) {
             throw new IllegalArgumentException("action cannot be null or an empty string");
         }
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index d2c614e..8c76665 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -338,7 +338,7 @@
     status_t res = anw->dequeueBuffer(anw.get(), &anb, &fenceFd);
     if (res != OK) {
         // TODO: handle different error cases here.
-        ALOGE("%s: Set buffer count failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+        ALOGE("%s: Dequeue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
         jniThrowRuntimeException(env, "dequeue buffer failed");
         return;
     }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 6f33672..d71b44b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -19,6 +19,7 @@
 import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.ICameraDeviceCallbacks;
@@ -170,7 +171,8 @@
         assertEquals(CameraBinderTestUtils.NO_ERROR, status);
         assertFalse(metadata.isEmpty());
 
-        CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false);
+        CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false,
+                CameraCaptureSession.SESSION_ID_NONE);
         assertFalse(request.isEmpty());
         assertFalse(metadata.isEmpty());
         if (needStream) {
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 2ec15be..aea8585 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
 
     <application android:label="@string/app_name" >
         <activity
@@ -28,9 +29,8 @@
             android:label="@string/action_bar_label"
             android:theme="@style/AppTheme" >
             <intent-filter>
-                <action android:name="android.intent.action.ACTION_SEND"/>
+                <action android:name="android.net.conn.CAPTIVE_PORTAL"/>
                 <category android:name="android.intent.category.DEFAULT"/>
-                <data android:mimeType="text/plain"/>
             </intent-filter>
         </activity>
     </application>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index e4054ac..b86fc4b 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -56,24 +56,13 @@
     private static final String DEFAULT_SERVER = "connectivitycheck.android.com";
     private static final int SOCKET_TIMEOUT_MS = 10000;
 
-    // Keep this in sync with NetworkMonitor.
-    // Intent broadcast to ConnectivityService indicating sign-in is complete.
-    // Extras:
-    //     EXTRA_TEXT       = netId
-    //     LOGGED_IN_RESULT = one of the CAPTIVE_PORTAL_APP_RETURN_* values below.
-    //     RESPONSE_TOKEN   = data fragment from launching Intent
-    private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
-            "android.net.netmon.captive_portal_logged_in";
-    private static final String LOGGED_IN_RESULT = "result";
-    private static final int CAPTIVE_PORTAL_APP_RETURN_APPEASED = 0;
-    private static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
-    private static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
-    private static final String RESPONSE_TOKEN = "response_token";
+    private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
 
     private URL mURL;
-    private int mNetId;
+    private Network mNetwork;
     private String mResponseToken;
     private NetworkCallback mNetworkCallback;
+    private ConnectivityManager mCm;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -81,23 +70,19 @@
 
         String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
         if (server == null) server = DEFAULT_SERVER;
+        mCm = ConnectivityManager.from(this);
         try {
             mURL = new URL("http", server, "/generate_204");
-            final Uri dataUri = getIntent().getData();
-            if (!dataUri.getScheme().equals("netid")) {
-                throw new MalformedURLException();
-            }
-            mNetId = Integer.parseInt(dataUri.getSchemeSpecificPart());
-            mResponseToken = dataUri.getFragment();
-        } catch (MalformedURLException|NumberFormatException e) {
+        } catch (MalformedURLException e) {
             // System misconfigured, bail out in a way that at least provides network access.
-            done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
+            Log.e(TAG, "Invalid captive portal URL, server=" + server);
+            done(Result.WANTED_AS_IS);
         }
+        mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
+        mResponseToken = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN);
 
-        final ConnectivityManager cm = ConnectivityManager.from(this);
-        final Network network = new Network(mNetId);
         // Also initializes proxy system properties.
-        cm.bindProcessToNetwork(network);
+        mCm.bindProcessToNetwork(mNetwork);
 
         // Proxy system properties must be initialized before setContentView is called because
         // setContentView initializes the WebView logic which in turn reads the system properties.
@@ -106,7 +91,7 @@
         getActionBar().setDisplayShowHomeEnabled(false);
 
         // Exit app if Network disappears.
-        final NetworkCapabilities networkCapabilities = cm.getNetworkCapabilities(network);
+        final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
         if (networkCapabilities == null) {
             finish();
             return;
@@ -114,14 +99,14 @@
         mNetworkCallback = new NetworkCallback() {
             @Override
             public void onLost(Network lostNetwork) {
-                if (network.equals(lostNetwork)) done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
+                if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
             }
         };
         final NetworkRequest.Builder builder = new NetworkRequest.Builder();
         for (int transportType : networkCapabilities.getTransportTypes()) {
             builder.addTransportType(transportType);
         }
-        cm.registerNetworkCallback(builder.build(), mNetworkCallback);
+        mCm.registerNetworkCallback(builder.build(), mNetworkCallback);
 
         final WebView myWebView = (WebView) findViewById(R.id.webview);
         myWebView.clearCache(true);
@@ -158,15 +143,21 @@
         }
     }
 
-    private void done(int result) {
+    private void done(Result result) {
         if (mNetworkCallback != null) {
-            ConnectivityManager.from(this).unregisterNetworkCallback(mNetworkCallback);
+            mCm.unregisterNetworkCallback(mNetworkCallback);
         }
-        Intent intent = new Intent(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
-        intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetId));
-        intent.putExtra(LOGGED_IN_RESULT, String.valueOf(result));
-        intent.putExtra(RESPONSE_TOKEN, mResponseToken);
-        sendBroadcast(intent);
+        switch (result) {
+            case DISMISSED:
+                mCm.reportCaptivePortalDismissed(mNetwork, mResponseToken);
+                break;
+            case UNWANTED:
+                mCm.ignoreNetworkWithCaptivePortal(mNetwork, mResponseToken);
+                break;
+            case WANTED_AS_IS:
+                mCm.useNetworkWithCaptivePortal(mNetwork, mResponseToken);
+                break;
+        }
         finish();
     }
 
@@ -190,11 +181,11 @@
     public boolean onOptionsItemSelected(MenuItem item) {
         int id = item.getItemId();
         if (id == R.id.action_use_network) {
-            done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
+            done(Result.WANTED_AS_IS);
             return true;
         }
         if (id == R.id.action_do_not_use_network) {
-            done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
+            done(Result.UNWANTED);
             return true;
         }
         return super.onOptionsItemSelected(item);
@@ -223,7 +214,7 @@
                     if (urlConnection != null) urlConnection.disconnect();
                 }
                 if (httpResponseCode == 204) {
-                    done(CAPTIVE_PORTAL_APP_RETURN_APPEASED);
+                    done(Result.DISMISSED);
                 }
             }
         }).start();
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index e0af29d..1cf7248 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -32,6 +32,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageVolume;
@@ -76,6 +77,9 @@
             Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
 
     public static class MeasurementDetails {
+        public long totalSize;
+        public long availSize;
+
         /**
          * Total apps disk usage.
          * <p>
@@ -121,7 +125,7 @@
     }
 
     public interface MeasurementReceiver {
-        public void onDetailsChanged(MeasurementDetails details);
+        void onDetailsChanged(MeasurementDetails details);
     }
 
     private WeakReference<MeasurementReceiver> mReceiver;
@@ -370,6 +374,10 @@
             }
         }
 
+        final File file = mVolume.getPath();
+        details.totalSize = file.getTotalSpace();
+        details.availSize = file.getFreeSpace();
+
         // Measure all apps hosted on this volume for all users
         if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
             final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 35e9636..5b4b4fd 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -94,6 +94,7 @@
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
     <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
+    <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
 
     <application android:label="@string/app_label">
         <provider
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 292c9c2..0d331d1 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -328,7 +328,7 @@
         int fillColor = getFillColor(darkIntensity);
         mIconTint = fillColor;
         mFramePaint.setColor(backgroundColor);
-        mBoltPaint.setColor(backgroundColor);
+        mBoltPaint.setColor(fillColor);
         mChargeColor = fillColor;
         invalidate();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b828e78..7b555fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1549,6 +1549,7 @@
             try {
                 callback.onSimSecureStateChanged(mUpdateMonitor.isSimPinSecure());
                 callback.onShowingStateChanged(mShowing);
+                callback.onInputRestrictedStateChanged(mInputRestricted);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failed to call onShowingStateChanged or onSimSecureStateChanged", e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index de4874f..e542264 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -700,6 +700,26 @@
         return isCurrentProfile(notificationUserId);
     }
 
+    protected void setNotificationShown(StatusBarNotification n) {
+        mNotificationListener.setNotificationsShown(new String[] { n.getKey() });
+    }
+
+    protected void setNotificationsShown(String[] keys) {
+        mNotificationListener.setNotificationsShown(keys);
+    }
+
+    protected void setNotificationsShownAll() {
+        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+        final int N = activeNotifications.size();
+
+        String[] keys = new String[N];
+        for (int i = 0; i < N; i++) {
+            NotificationData.Entry entry = activeNotifications.get(i);
+            keys[i] = entry.key;
+        }
+        setNotificationsShown(keys);
+    }
+
     protected boolean isCurrentProfile(int userId) {
         synchronized (mCurrentProfiles) {
             return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
@@ -1681,6 +1701,7 @@
                 boolean clearNotificationEffects =
                         (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
                 mBarService.onPanelRevealed(clearNotificationEffects);
+                setNotificationsShownAll();
             } else {
                 mBarService.onPanelHidden();
             }
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 c854d63..d0588924 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1119,6 +1119,8 @@
         boolean isHeadsUped = mUseHeadsUp && shouldInterrupt(notification);
         if (isHeadsUped) {
             mHeadsUpManager.showNotification(shadeEntry);
+            // Mark as seen immediately
+            setNotificationShown(notification);
         }
 
         if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
diff --git a/preloaded-classes b/preloaded-classes
index 95d0b42..c94623a 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -402,6 +402,7 @@
 android.app.SharedPreferencesImpl$EditorImpl$1
 android.app.SharedPreferencesImpl$EditorImpl$2
 android.app.SharedPreferencesImpl$MemoryCommitResult
+android.app.SystemServiceRegistry
 android.app.admin.DevicePolicyManager
 android.app.admin.IDevicePolicyManager$Stub
 android.app.admin.IDevicePolicyManager$Stub$Proxy
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 3e5eee8..ef82bb7 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -56,6 +56,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import java.util.*;
 class BluetoothManagerService extends IBluetoothManager.Stub {
     private static final String TAG = "BluetoothManagerService";
     private static final boolean DBG = true;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7d8e9de..12a99b0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2608,6 +2608,16 @@
         }
     }
 
+    public void captivePortalAppResponse(Network network, int response, String actionToken) {
+        if (response == ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS) {
+            enforceConnectivityInternalPermission();
+        }
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai == null) return;
+        nai.networkMonitor.sendMessage(NetworkMonitor.CMD_CAPTIVE_PORTAL_APP_FINISHED, response, 0,
+                actionToken);
+    }
+
     public ProxyInfo getDefaultProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 89a7173..7172ab7 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -30,6 +30,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.IPackageMoveObserver;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.ObbInfo;
 import android.mtp.MtpStorage;
@@ -178,6 +180,9 @@
     /** Maximum number of ASEC containers allowed to be mounted. */
     private static final int MAX_CONTAINERS = 250;
 
+    /** Magic value sent by MoveTask.cpp */
+    private static final int MOVE_STATUS_COPY_FINISHED = 82;
+
     /*
      * Internal vold response code constants
      */
@@ -226,6 +231,8 @@
         public static final int VOLUME_PATH_CHANGED = 655;
         public static final int VOLUME_DESTROYED = 659;
 
+        public static final int MOVE_STATUS = 660;
+
         /*
          * 700 series - fstrim
          */
@@ -314,6 +321,11 @@
     @GuardedBy("mLock")
     private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
 
+    @GuardedBy("mLock")
+    private IPackageMoveObserver mMoveCallback;
+    @GuardedBy("mLock")
+    private String mMoveTargetUuid;
+
     private DiskInfo findDiskById(String id) {
         synchronized (mLock) {
             final DiskInfo disk = mDisks.get(id);
@@ -347,6 +359,17 @@
         throw new IllegalArgumentException("No volume found for path " + path);
     }
 
+    private VolumeInfo findStorageForUuid(String volumeUuid) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
+            return findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL);
+        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
+            return storage.getPrimaryPhysicalVolume();
+        } else {
+            return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid));
+        }
+    }
+
     private VolumeMetadata findOrCreateMetadataLocked(VolumeInfo vol) {
         VolumeMetadata meta = mMetadata.get(vol.fsUuid);
         if (meta == null) {
@@ -937,6 +960,12 @@
                 break;
             }
 
+            case VoldResponseCode.MOVE_STATUS: {
+                final int status = Integer.parseInt(cooked[1]);
+                onMoveStatusLocked(status);
+                break;
+            }
+
             case VoldResponseCode.FstrimCompleted: {
                 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
                 break;
@@ -972,24 +1001,36 @@
     }
 
     private void onVolumeCreatedLocked(VolumeInfo vol) {
-        final boolean primaryPhysical = SystemProperties.getBoolean(
-                StorageManager.PROP_PRIMARY_PHYSICAL, false);
-        // TODO: enable switching to another emulated primary
-        if (VolumeInfo.ID_EMULATED_INTERNAL.equals(vol.id) && !primaryPhysical) {
-            vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
-            vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
-            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
+        if (vol.type == VolumeInfo.TYPE_EMULATED) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
+
+            if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
+                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
+                Slog.v(TAG, "Found primary storage at " + vol);
+                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
+                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
+
+            } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
+                Slog.v(TAG, "Found primary storage at " + vol);
+                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
+                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
+                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
+            }
 
         } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
-            if (primaryPhysical) {
+            // TODO: only look at first public partition
+            if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
+                    && vol.disk.isDefaultPrimary()) {
+                Slog.v(TAG, "Found primary storage at " + vol);
                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
             }
 
             // Adoptable public disks are visible to apps, since they meet
             // public API requirement of being in a stable location.
-            final DiskInfo disk = mDisks.get(vol.getDiskId());
-            if (disk != null && disk.isAdoptable()) {
+            if (vol.disk.isAdoptable()) {
                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
             }
 
@@ -1066,6 +1107,35 @@
         }
     }
 
+    private void onMoveStatusLocked(int status) {
+        if (mMoveCallback == null) {
+            Slog.w(TAG, "Odd, status but no move requested");
+            return;
+        }
+
+        // TODO: estimate remaining time
+        try {
+            mMoveCallback.onStatusChanged(-1, status, -1);
+        } catch (RemoteException ignored) {
+        }
+
+        // We've finished copying and we're about to clean up old data, so
+        // remember that move was successful if we get rebooted
+        if (status == MOVE_STATUS_COPY_FINISHED) {
+            Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting");
+
+            mPrimaryStorageUuid = mMoveTargetUuid;
+            writeMetadataLocked();
+        }
+
+        if (PackageManager.isMoveStatusFinished(status)) {
+            Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status);
+
+            mMoveCallback = null;
+            mMoveTargetUuid = null;
+        }
+    }
+
     /**
      * Refresh latest metadata into any currently active {@link VolumeInfo}.
      */
@@ -1322,12 +1392,17 @@
         final VolumeInfo vol = findVolumeById(volId);
 
         // TODO: expand PMS to know about multiple volumes
-        if (vol.isPrimary()) {
-            synchronized (mUnmountLock) {
-                mUnmountSignal = new CountDownLatch(1);
-                mPms.updateExternalMediaStatus(false, true);
-                waitForLatch(mUnmountSignal, "mUnmountSignal");
-                mUnmountSignal = null;
+        if (vol.isPrimaryPhysical()) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                synchronized (mUnmountLock) {
+                    mUnmountSignal = new CountDownLatch(1);
+                    mPms.updateExternalMediaStatus(false, true);
+                    waitForLatch(mUnmountSignal, "mUnmountSignal");
+                    mUnmountSignal = null;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
         }
 
@@ -1424,20 +1499,41 @@
     }
 
     @Override
-    public String getPrimaryStorageUuid() throws RemoteException {
+    public String getPrimaryStorageUuid() {
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        waitForReady();
+
         synchronized (mLock) {
             return mPrimaryStorageUuid;
         }
     }
 
     @Override
-    public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException {
-        synchronized (mLock) {
-            Slog.d(TAG, "Changing primary storage UUID to " + volumeUuid);
-            mPrimaryStorageUuid = volumeUuid;
-            writeMetadataLocked();
+    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) {
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        waitForReady();
 
-            // TODO: reevaluate all volumes we know about!
+        synchronized (mLock) {
+            final VolumeInfo from = Preconditions.checkNotNull(
+                    findStorageForUuid(mPrimaryStorageUuid));
+            final VolumeInfo to = Preconditions.checkNotNull(
+                    findStorageForUuid(volumeUuid));
+
+            if (Objects.equals(from, to)) {
+                throw new IllegalArgumentException("Primary storage already at " + from);
+            }
+
+            if (mMoveCallback != null) {
+                throw new IllegalStateException("Move already in progress");
+            }
+            mMoveCallback = callback;
+            mMoveTargetUuid = volumeUuid;
+
+            try {
+                mConnector.execute("volume", "move_storage", from.id, to.id);
+            } catch (NativeDaemonConnectorException e) {
+                throw e.rethrowAsParcelableException();
+            }
         }
     }
 
@@ -1758,6 +1854,9 @@
 
     @Override
     public void finishMediaUpdate() {
+        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+            throw new SecurityException("no permission to call finishMediaUpdate()");
+        }
         if (mUnmountSignal != null) {
             mUnmountSignal.countDown();
         } else {
@@ -1791,7 +1890,7 @@
         warnOnNotMounted();
 
         final ObbState state;
-        synchronized (mObbPathToStateMap) {
+        synchronized (mObbMounts) {
             state = mObbPathToStateMap.get(rawPath);
         }
         if (state == null) {
@@ -1843,7 +1942,7 @@
         Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
 
         final ObbState existingState;
-        synchronized (mObbPathToStateMap) {
+        synchronized (mObbMounts) {
             existingState = mObbPathToStateMap.get(rawPath);
         }
 
@@ -2093,6 +2192,8 @@
 
     @Override
     public String getPassword() throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
+                "only keyguard can retrieve password");
         if (!isReady()) {
             return new String();
         }
@@ -2870,6 +2971,9 @@
                 meta.dump(pw);
             }
             pw.decreaseIndent();
+
+            pw.println();
+            pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
         }
 
         synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1b32f57..999e91b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -734,12 +734,15 @@
             throw new IllegalArgumentException("account is null");
         }
         checkAuthenticateAccountsPermission(account);
-
-        final UserAccounts accounts = getUserAccountsForCaller();
         int userId = Binder.getCallingUserHandle().getIdentifier();
         if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
             return false;
         }
+        return updateLastAuthenticatedTime(account);
+    }
+
+    private boolean updateLastAuthenticatedTime(Account account) {
+        final UserAccounts accounts = getUserAccountsForCaller();
         synchronized (accounts.cacheLock) {
             final ContentValues values = new ContentValues();
             values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
@@ -2022,7 +2025,7 @@
         try {
             new Session(accounts, response, account.type, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */, account.name,
-                    true /* authDetailsRequired */) {
+                    true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.confirmCredentials(this, account, options);
@@ -2059,7 +2062,7 @@
         try {
             new Session(accounts, response, account.type, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */, account.name,
-                    false /* authDetailsRequired */) {
+                    false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
@@ -2492,6 +2495,11 @@
         final String mAccountName;
         // Indicates if we need to add auth details(like last credential time)
         final boolean mAuthDetailsRequired;
+        // If set, we need to update the last authenticated time. This is
+        // currently
+        // used on
+        // successful confirming credentials.
+        final boolean mUpdateLastAuthenticatedTime;
 
         public int mNumResults = 0;
         private int mNumRequestContinued = 0;
@@ -2505,6 +2513,13 @@
         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
                 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
                 boolean authDetailsRequired) {
+            this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
+                    accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
+        }
+
+        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
+                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
+                boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
             super();
             //if (response == null) throw new IllegalArgumentException("response is null");
             if (accountType == null) throw new IllegalArgumentException("accountType is null");
@@ -2516,6 +2531,7 @@
             mCreationTime = SystemClock.elapsedRealtime();
             mAccountName = accountName;
             mAuthDetailsRequired = authDetailsRequired;
+            mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
 
             synchronized (mSessions) {
                 mSessions.put(toString(), this);
@@ -2651,15 +2667,55 @@
         public void onResult(Bundle result) {
             mNumResults++;
             Intent intent = null;
-            if (result != null && mAuthDetailsRequired) {
-                long lastAuthenticatedTime = DatabaseUtils.longForQuery(
-                        mAccounts.openHelper.getReadableDatabase(),
-                        "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from " +
-                                TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
-                                + ACCOUNTS_TYPE + "=?",
-                        new String[]{mAccountName, mAccountType});
-                result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
-                        lastAuthenticatedTime);
+            if (result != null) {
+                boolean isSuccessfulConfirmCreds = result.getBoolean(
+                        AccountManager.KEY_BOOLEAN_RESULT, false);
+                boolean isSuccessfulUpdateCreds = 
+                        result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
+                        && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
+                // We should only update lastAuthenticated time, if 
+                // mUpdateLastAuthenticatedTime is true and the confirmRequest
+                // or updateRequest was successful
+                boolean needUpdate = mUpdateLastAuthenticatedTime 
+                        && (isSuccessfulConfirmCreds || isSuccessfulUpdateCreds);
+                if (needUpdate || mAuthDetailsRequired) {
+                    boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
+                    if (needUpdate && accountPresent) {
+                        updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
+                    }
+                    if (mAuthDetailsRequired) {
+                        long lastAuthenticatedTime = -1;
+                        if (accountPresent) {
+                            lastAuthenticatedTime = DatabaseUtils.longForQuery(
+                                    mAccounts.openHelper.getReadableDatabase(),
+                                    "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+                                            + " from " +
+                                            TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+                                            + ACCOUNTS_TYPE + "=?",
+                                    new String[] {
+                                            mAccountName, mAccountType
+                                    });
+                        }
+                        result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+                                lastAuthenticatedTime);
+                    }
+                }
+                if (mAuthDetailsRequired) {
+                    long lastAuthenticatedTime = -1;
+                    if (isAccountPresentForCaller(mAccountName, mAccountType)) {
+                        lastAuthenticatedTime = DatabaseUtils.longForQuery(
+                                mAccounts.openHelper.getReadableDatabase(),
+                                "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from "
+                                        +
+                                        TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+                                        + ACCOUNTS_TYPE + "=?",
+                                new String[] {
+                                        mAccountName, mAccountType
+                                });
+                    }
+                    result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+                            lastAuthenticatedTime);
+                }
             }
             if (result != null
                     && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
@@ -3202,6 +3258,17 @@
         return false;
     }
 
+    private boolean isAccountPresentForCaller(String accountName, String accountType) {
+        if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
+            for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
+                if (account.name.equals(accountName)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
             int callerUid) {
         if (callerUid == Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a48a4d9..4970e0f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5508,17 +5508,20 @@
             if (app.isolated) {
                 mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
             }
-            app.kill(reason, true);
-            handleAppDiedLocked(app, true, allowRestart);
-            removeLruProcessLocked(app);
-
+            boolean willRestart = false;
             if (app.persistent && !app.isolated) {
                 if (!callerWillRestart) {
-                    addAppLocked(app.info, false, null /* ABI override */);
+                    willRestart = true;
                 } else {
                     needRestart = true;
                 }
             }
+            app.kill(reason, true);
+            handleAppDiedLocked(app, willRestart, allowRestart);
+            if (willRestart) {
+                removeLruProcessLocked(app);
+                addAppLocked(app.info, false, null /* ABI override */);
+            }
         } else {
             mRemovedProcesses.add(app);
         }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9a30f0d..6b56279 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -86,6 +86,8 @@
 import android.provider.Settings.System;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
@@ -110,10 +112,8 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
-import java.util.Set;
 
 /**
  * The implementation of the volume manager service.
@@ -407,8 +407,7 @@
         return "0x" + Integer.toHexString(device) + ":" + deviceAddress;
     }
 
-    private final HashMap<String, DeviceListSpec> mConnectedDevices =
-            new HashMap<String, DeviceListSpec>();
+    private final ArrayMap<String, DeviceListSpec> mConnectedDevices = new ArrayMap<>();
 
     // Forced device usage for communications
     private int mForcedUseForComm;
@@ -2830,16 +2829,22 @@
             }
         }
         public void onServiceDisconnected(int profile) {
+            ArraySet<String> toRemove = null;
             switch (profile) {
             case BluetoothProfile.A2DP:
                 synchronized (mConnectedDevices) {
                     synchronized (mA2dpAvrcpLock) {
                         // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
-                        for(Map.Entry<String, DeviceListSpec> entry
-                                : mConnectedDevices.entrySet()) {
-                            DeviceListSpec deviceSpec = entry.getValue();
+                        for (int i = 0; i < mConnectedDevices.size(); i++) {
+                            DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
                             if (deviceSpec.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
-                                makeA2dpDeviceUnavailableNow(deviceSpec.mDeviceAddress);
+                                toRemove = toRemove != null ? toRemove : new ArraySet<String>();
+                                toRemove.add(deviceSpec.mDeviceAddress);
+                            }
+                        }
+                        if (toRemove != null) {
+                            for (int i = 0; i < toRemove.size(); i++) {
+                                makeA2dpDeviceUnavailableNow(toRemove.valueAt(i));
                             }
                         }
                     }
@@ -2849,11 +2854,16 @@
             case BluetoothProfile.A2DP_SINK:
                 synchronized (mConnectedDevices) {
                     // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices
-                    for(Map.Entry<String, DeviceListSpec> entry
-                            : mConnectedDevices.entrySet()) {
-                        DeviceListSpec deviceSpec = entry.getValue();
+                    for(int i = 0; i < mConnectedDevices.size(); i++) {
+                        DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
                         if (deviceSpec.mDeviceType == AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) {
-                            makeA2dpSrcUnavailable(deviceSpec.mDeviceAddress);
+                            toRemove = toRemove != null ? toRemove : new ArraySet<String>();
+                            toRemove.add(deviceSpec.mDeviceAddress);
+                        }
+                    }
+                    if (toRemove != null) {
+                        for (int i = 0; i < toRemove.size(); i++) {
+                            makeA2dpSrcUnavailable(toRemove.valueAt(i));
                         }
                     }
                 }
@@ -4147,11 +4157,8 @@
 
                     // Restore device connection states
                     synchronized (mConnectedDevices) {
-                        Set set = mConnectedDevices.entrySet();
-                        Iterator i = set.iterator();
-                        while (i.hasNext()) {
-                            Map.Entry device = (Map.Entry)i.next();
-                            DeviceListSpec spec = (DeviceListSpec)device.getValue();
+                        for (int i = 0; i < mConnectedDevices.size(); i++) {
+                            DeviceListSpec spec = mConnectedDevices.valueAt(i);
                             AudioSystem.setDeviceConnectionState(
                                                             spec.mDeviceType,
                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
@@ -4600,8 +4607,8 @@
         int delay = 0;
         if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
             int devices = 0;
-            for (String key : mConnectedDevices.keySet()) {
-                int dev = mConnectedDevices.get(key).mDeviceType;
+            for (int i = 0; i < mConnectedDevices.size(); i++) {
+                int dev = mConnectedDevices.valueAt(i).mDeviceType;
                 if (((dev & AudioSystem.DEVICE_BIT_IN) == 0)
                         && ((dev & mBecomingNoisyIntentDevices) != 0)) {
                     devices |= dev;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 7e20276..4e83992 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -83,17 +83,6 @@
     private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
             "android.permission.ACCESS_NETWORK_CONDITIONS";
 
-    // Keep these in sync with CaptivePortalLoginActivity.java.
-    // Intent broadcast from CaptivePortalLogin indicating sign-in is complete.
-    // Extras:
-    //     EXTRA_TEXT       = netId
-    //     LOGGED_IN_RESULT = one of the CAPTIVE_PORTAL_APP_RETURN_* values below.
-    //     RESPONSE_TOKEN   = data fragment from launching Intent
-    private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
-            "android.net.netmon.captive_portal_logged_in";
-    private static final String LOGGED_IN_RESULT = "result";
-    private static final String RESPONSE_TOKEN = "response_token";
-
     // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
     // The network should be used as a default internet connection.  It was found to be:
     // 1. a functioning network providing internet access, or
@@ -166,11 +155,12 @@
 
     /**
      * Message to self indicating captive portal app finished.
-     * arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_APPEASED,
+     * arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_DISMISSED,
      *                CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
      *                CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS
+     * obj = mCaptivePortalLoggedInResponseToken as String
      */
-    private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
+    public static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
 
     /**
      * Request ConnectivityService display provisioning notification.
@@ -181,26 +171,11 @@
     public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 10;
 
     /**
-     * Message to self indicating sign-in app bypassed captive portal.
+     * Message to self indicating sign-in app should be launched.
+     * Sent by mLaunchCaptivePortalAppBroadcastReceiver when the
+     * user touches the sign in notification.
      */
-    private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 11;
-
-    /**
-     * Message to self indicating no sign-in app responded.
-     */
-    private static final int EVENT_NO_APP_RESPONSE = BASE + 12;
-
-    /**
-     * Message to self indicating sign-in app indicates sign-in is not possible.
-     */
-    private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 13;
-
-    /**
-     * Return codes from captive portal sign-in app.
-     */
-    public static final int CAPTIVE_PORTAL_APP_RETURN_APPEASED = 0;
-    public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
-    public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
+    private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
 
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     // Default to 30s linger time-out.
@@ -255,7 +230,7 @@
     private final State mCaptivePortalState = new CaptivePortalState();
     private final State mLingeringState = new LingeringState();
 
-    private CaptivePortalLoggedInBroadcastReceiver mCaptivePortalLoggedInBroadcastReceiver = null;
+    private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
     private String mCaptivePortalLoggedInResponseToken = null;
 
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
@@ -319,9 +294,9 @@
                     return HANDLED;
                 case CMD_NETWORK_DISCONNECTED:
                     if (DBG) log("Disconnected - quitting");
-                    if (mCaptivePortalLoggedInBroadcastReceiver != null) {
-                        mContext.unregisterReceiver(mCaptivePortalLoggedInBroadcastReceiver);
-                        mCaptivePortalLoggedInBroadcastReceiver = null;
+                    if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
+                        mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
+                        mLaunchCaptivePortalAppBroadcastReceiver = null;
                     }
                     quit();
                     return HANDLED;
@@ -332,14 +307,21 @@
                     transitionTo(mEvaluatingState);
                     return HANDLED;
                 case CMD_CAPTIVE_PORTAL_APP_FINISHED:
-                    // Previous token was broadcast, come up with a new one.
+                    if (!mCaptivePortalLoggedInResponseToken.equals((String)message.obj))
+                        return HANDLED;
+                    // Previous token was sent out, come up with a new one.
                     mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong());
                     switch (message.arg1) {
-                        case CAPTIVE_PORTAL_APP_RETURN_APPEASED:
-                        case CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_DISMISSED:
+                            sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */,
+                                    0 /* INITIAL_ATTEMPTS */);
+                            break;
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
+                            // TODO: Distinguish this from a network that actually validates.
+                            // Displaying the "!" on the system UI icon may still be a good idea.
                             transitionTo(mValidatedState);
                             break;
-                        case CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
                             mUserDoesNotWant = true;
                             // TODO: Should teardown network.
                             transitionTo(mOfflineState);
@@ -417,6 +399,25 @@
     // is required.  This State takes care to clear the notification upon exit from the State.
     private class MaybeNotifyState extends State {
         @Override
+        public boolean processMessage(Message message) {
+            if (DBG) log(getName() + message.toString());
+            switch (message.what) {
+                case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
+                    final Intent intent = new Intent(
+                            ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+                    intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mNetworkAgentInfo.network);
+                    intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN,
+                            mCaptivePortalLoggedInResponseToken);
+                    intent.setFlags(
+                            Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
+                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+
+        @Override
         public void exit() {
             Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
                     mNetworkAgentInfo.network.netId, null);
@@ -512,7 +513,9 @@
             mContext.registerReceiver(this, new IntentFilter(mAction));
         }
         public PendingIntent getPendingIntent() {
-            return PendingIntent.getBroadcast(mContext, 0, new Intent(mAction), 0);
+            final Intent intent = new Intent(mAction);
+            intent.setPackage(mContext.getPackageName());
+            return PendingIntent.getBroadcast(mContext, 0, intent, 0);
         }
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -520,48 +523,29 @@
         }
     }
 
-    private class CaptivePortalLoggedInBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) ==
-                    mNetworkAgentInfo.network.netId &&
-                    mCaptivePortalLoggedInResponseToken.equals(
-                            intent.getStringExtra(RESPONSE_TOKEN))) {
-                sendMessage(obtainMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED,
-                        Integer.parseInt(intent.getStringExtra(LOGGED_IN_RESULT)), 0));
-            }
-        }
-    }
-
     // Being in the CaptivePortalState State indicates a captive portal was detected and the user
     // has been shown a notification to sign-in.
     private class CaptivePortalState extends State {
+        private static final String ACTION_LAUNCH_CAPTIVE_PORTAL_APP =
+                "android.net.netmon.launchCaptivePortalApp";
+
         @Override
         public void enter() {
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                     NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
-
-            // Assemble Intent to launch captive portal sign-in app.
-            final Intent intent = new Intent(Intent.ACTION_SEND);
-            // Intent cannot use extras because PendingIntent.getActivity will merge matching
-            // Intents erasing extras.  Use data instead of extras to encode NetID.
-            intent.setData(Uri.fromParts("netid", Integer.toString(mNetworkAgentInfo.network.netId),
-                    mCaptivePortalLoggedInResponseToken));
-            intent.setComponent(new ComponentName("com.android.captiveportallogin",
-                    "com.android.captiveportallogin.CaptivePortalLoginActivity"));
-            intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            if (mCaptivePortalLoggedInBroadcastReceiver == null) {
+            // Create a CustomIntentReceiver that sends us a
+            // CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user
+            // touches the notification.
+            if (mLaunchCaptivePortalAppBroadcastReceiver == null) {
                 // Wait for result.
-                mCaptivePortalLoggedInBroadcastReceiver =
-                        new CaptivePortalLoggedInBroadcastReceiver();
-                final IntentFilter filter = new IntentFilter(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
-                mContext.registerReceiver(mCaptivePortalLoggedInBroadcastReceiver, filter);
+                mLaunchCaptivePortalAppBroadcastReceiver = new CustomIntentReceiver(
+                        ACTION_LAUNCH_CAPTIVE_PORTAL_APP, new Random().nextInt(),
+                        CMD_LAUNCH_CAPTIVE_PORTAL_APP);
             }
-            // Initiate notification to sign-in.
+            // Display the sign in notification.
             Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
                     mNetworkAgentInfo.network.netId,
-                    PendingIntent.getActivity(mContext, 0, intent, 0));
+                    mLaunchCaptivePortalAppBroadcastReceiver.getPendingIntent());
             mConnectivityServiceHandler.sendMessage(message);
         }
 
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 28597c1..6b5908d 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -37,7 +37,6 @@
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -364,12 +363,12 @@
 
     private class ClientMonitor implements IBinder.DeathRecipient {
         IBinder token;
-        WeakReference<IFingerprintServiceReceiver> receiver;
+        IFingerprintServiceReceiver receiver;
         int userId;
 
         public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
             this.token = token;
-            this.receiver = new WeakReference<IFingerprintServiceReceiver>(receiver);
+            this.receiver = receiver;
             this.userId = userId;
             try {
                 token.linkToDeath(this, 0);
@@ -389,6 +388,7 @@
         public void binderDied() {
             token = null;
             removeClient(this);
+            receiver = null;
         }
 
         protected void finalize() throws Throwable {
@@ -406,10 +406,9 @@
          * @return true if we're done.
          */
         private boolean sendRemoved(int fingerId, int groupId) {
-            IFingerprintServiceReceiver rx = receiver.get();
-            if (rx == null) return true; // client not listening
+            if (receiver == null) return true; // client not listening
             try {
-                rx.onRemoved(mHalDeviceId, fingerId, groupId);
+                receiver.onRemoved(mHalDeviceId, fingerId, groupId);
                 return fingerId == 0;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failed to notify Removed:", e);
@@ -421,11 +420,10 @@
          * @return true if we're done.
          */
         private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
-            IFingerprintServiceReceiver rx = receiver.get();
-            if (rx == null) return true; // client not listening
+            if (receiver == null) return true; // client not listening
             FingerprintUtils.vibrateFingerprintSuccess(getContext());
             try {
-                rx.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
+                receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
                 return remaining == 0;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failed to notify EnrollResult:", e);
@@ -437,11 +435,10 @@
          * @return true if we're done.
          */
         private boolean sendAuthenticated(int fpId, int groupId) {
-            IFingerprintServiceReceiver rx = receiver.get();
             boolean result = false;
-            if (rx != null) {
+            if (receiver != null) {
                 try {
-                    rx.onAuthenticated(mHalDeviceId, fpId, groupId);
+                    receiver.onAuthenticated(mHalDeviceId, fpId, groupId);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed to notify Authenticated:", e);
                     result = true; // client failed
@@ -464,10 +461,9 @@
          * @return true if we're done.
          */
         private boolean sendAcquired(int acquiredInfo) {
-            IFingerprintServiceReceiver rx = receiver.get();
-            if (rx == null) return true; // client not listening
+            if (receiver == null) return true; // client not listening
             try {
-                rx.onAcquired(mHalDeviceId, acquiredInfo);
+                receiver.onAcquired(mHalDeviceId, acquiredInfo);
                 return false; // acquisition continues...
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failed to invoke sendAcquired:", e);
@@ -479,10 +475,9 @@
          * @return true if we're done.
          */
         private boolean sendError(int error) {
-            IFingerprintServiceReceiver rx = receiver.get();
-            if (rx != null) {
+            if (receiver != null) {
                 try {
-                    rx.onError(mHalDeviceId, error);
+                    receiver.onError(mHalDeviceId, error);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed to invoke sendError:", e);
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1008653..25998da 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -36,6 +36,9 @@
 import android.app.NotificationManager.Policy;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -97,6 +100,7 @@
 import com.android.internal.R;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
@@ -236,6 +240,7 @@
     ArrayList<String> mLights = new ArrayList<>();
 
     private AppOpsManager mAppOps;
+    private UsageStatsManagerInternal mAppUsageStats;
 
     private Archive mArchive;
 
@@ -871,6 +876,7 @@
         mAm = ActivityManagerNative.getDefault();
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
+        mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
 
         mHandler = new WorkerHandler();
         mRankingThread.start();
@@ -1405,6 +1411,41 @@
             }
         }
 
+        @Override
+        public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mNotificationList) {
+                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                    if (keys != null) {
+                        final int N = keys.length;
+                        for (int i = 0; i < N; i++) {
+                            NotificationRecord r = mNotificationsByKey.get(keys[i]);
+                            if (r == null) continue;
+                            final int userId = r.sbn.getUserId();
+                            if (userId != info.userid && userId != UserHandle.USER_ALL &&
+                                    !mUserProfiles.isCurrentProfile(userId)) {
+                                throw new SecurityException("Disallowed call from listener: "
+                                        + info.service);
+                            }
+                            if (!r.isSeen()) {
+                                if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
+                                mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+                                        userId == UserHandle.USER_ALL ? UserHandle.USER_OWNER
+                                                : userId,
+                                        UsageEvents.Event.INTERACTION);
+                                r.setSeen();
+                            }
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
         private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
                 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 5569a09..e106a4a 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -50,6 +50,8 @@
     NotificationUsageStats.SingleNotificationStats stats;
     boolean isCanceled;
     int score;
+    /** Whether the notification was seen by the user via one of the notification listeners. */
+    boolean mIsSeen;
 
     // These members are used by NotificationSignalExtractors
     // to communicate with the ranking module.
@@ -301,6 +303,16 @@
         return mGlobalSortKey;
     }
 
+    /** Check if any of the listeners have marked this notification as seen by the user. */
+    public boolean isSeen() {
+        return mIsSeen;
+    }
+
+    /** Mark the notification as seen by the user. */
+    public void setSeen() {
+        mIsSeen = true;
+    }
+
     public void setAuthoritativeRank(int authoritativeRank) {
         mAuthoritativeRank = authoritativeRank;
     }
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index db3ae91..c8e5c3a 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -488,7 +488,7 @@
     }
 
     public String encodePublicKey(PublicKey k) throws IOException {
-        return new String(Base64.encode(k.getEncoded(), 0));
+        return new String(Base64.encode(k.getEncoded(), Base64.NO_WRAP));
     }
 
     public void dumpLPr(PrintWriter pw, String packageName,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bd22524..f087c33 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -14307,12 +14307,22 @@
     public int movePrimaryStorage(String volumeUuid) throws RemoteException {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
 
-        final int moveId = mNextMoveId.getAndIncrement();
+        final int realMoveId = mNextMoveId.getAndIncrement();
+        final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() {
+            @Override
+            public void onStarted(int moveId, String title) {
+                // Ignored
+            }
 
-        // TODO: ask mountservice to take down both, connect over to DCS to
-        // migrate, and then bring up new storage
+            @Override
+            public void onStatusChanged(int moveId, int status, long estMillis) {
+                mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis);
+            }
+        };
 
-        return moveId;
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        storage.setPrimaryStorageUuid(volumeUuid, callback);
+        return realMoveId;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 25857c5..5536d4d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2151,6 +2151,16 @@
 
             win.setType(
                 WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
+
+            synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
+                // Assumes it's safe to show starting windows of launched apps while
+                // the keyguard is being hidden. This is okay because starting windows never show
+                // secret information.
+                if (mKeyguardHidden) {
+                    windowFlags |= FLAG_SHOW_WHEN_LOCKED;
+                }
+            }
+
             // Force the window flags: this is a fake window, so it is not really
             // touchable or focusable by the user.  We also add in the ALT_FOCUSABLE_IM
             // flag because we do know that the next window will take input
@@ -4131,6 +4141,12 @@
         if (attrs.type == TYPE_STATUS_BAR && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
             mForceStatusBarFromKeyguard = true;
         }
+
+        boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
+                && attrs.type < FIRST_SYSTEM_WINDOW;
+        final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
+        final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0;
+
         if (mTopFullscreenOpaqueWindowState == null &&
                 win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()) {
             if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
@@ -4143,8 +4159,6 @@
             if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
                 mShowingLockscreen = true;
             }
-            boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
-                    && attrs.type < FIRST_SYSTEM_WINDOW;
             if (attrs.type == TYPE_DREAM) {
                 // If the lockscreen was showing when the dream started then wait
                 // for the dream to draw before hiding the lockscreen.
@@ -4155,8 +4169,6 @@
                 }
             }
 
-            final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
-            final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0;
             if (appWindow) {
                 final IApplicationToken appToken = win.getAppToken();
                 if (showWhenLocked) {
@@ -4210,10 +4222,20 @@
                 }
 
                 if (mWinShowWhenLocked != null &&
-                        mWinShowWhenLocked.getAppToken() != win.getAppToken()) {
+                        mWinShowWhenLocked.getAppToken() != win.getAppToken() &&
+                        (attrs.flags & FLAG_SHOW_WHEN_LOCKED) == 0) {
                     win.hideLw(false);
                 }
             }
+        } else if (mTopFullscreenOpaqueWindowState == null && mWinShowWhenLocked == null) {
+            // No TopFullscreenOpaqueWindow is showing, but we found a SHOW_WHEN_LOCKED window
+            // that is being hidden in an animation - keep the
+            // keyguard hidden until the new window shows up and
+            // we know whether to show the keyguard or not.
+            if (win.isAnimatingLw() && appWindow && showWhenLocked) {
+                mHideLockScreen = true;
+                mWinShowWhenLocked = win;
+            }
         }
         if (mTopFullscreenOpaqueOrDimmingWindowState == null
                 && win.isVisibleOrBehindKeyguardLw() && !win.isGoneForLayoutLw()
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e914cd4..a210223 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -281,7 +281,7 @@
                 Slog.w(WindowManagerService.TAG, "removeAllWindows: removing win=" + win);
             }
 
-            service.removeWindowLocked(win.mSession, win);
+            service.removeWindowLocked(win);
         }
         allAppWindows.clear();
         windows.clear();
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index bb53534..c24fcb3 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -78,7 +78,7 @@
             WindowState windowState = (WindowState) inputWindowHandle.windowState;
             if (windowState != null) {
                 Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
-                mService.removeWindowLocked(windowState.mSession, windowState);
+                mService.removeWindowLocked(windowState);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 897b865..52071cc 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -201,9 +201,11 @@
                 null : winShowWhenLocked.mAppToken;
         final boolean hideWhenLocked =
                 !(((win.mIsImWindow || imeTarget == win) && showImeOverKeyguard)
-                        || (appShowWhenLocked != null && (appShowWhenLocked == win.mAppToken ||
+                        || (appShowWhenLocked != null && (appShowWhenLocked == win.mAppToken
+                        // Show all SHOW_WHEN_LOCKED windows while they're animating
+                        || (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.isAnimatingLw()
                         // Show error dialogs over apps that dismiss keyguard.
-                        (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0)));
+                        || (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0)));
         return ((mForceHiding == KEYGUARD_ANIMATING_IN)
                 && (!win.mWinAnimator.isAnimating() || hideWhenLocked))
                 || ((mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 38fc959..c5bdbb0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2660,11 +2660,11 @@
             if (win == null) {
                 return;
             }
-            removeWindowLocked(session, win);
+            removeWindowLocked(win);
         }
     }
 
-    public void removeWindowLocked(Session session, WindowState win) {
+    void removeWindowLocked(WindowState win) {
         if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
             if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cd9689e..ec70879 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1169,10 +1169,10 @@
                     WindowState win = mService.windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
-                        mService.removeWindowLocked(mSession, win);
+                        mService.removeWindowLocked(win);
                     } else if (mHasSurface) {
                         Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
-                        mService.removeWindowLocked(mSession, WindowState.this);
+                        mService.removeWindowLocked(WindowState.this);
                     }
                 }
             } catch (IllegalArgumentException ex) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index bbeee8c..b303505 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -83,7 +83,7 @@
             WindowState win = windows.get(winNdx);
             if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) Slog.w(WindowManagerService.TAG,
                     "removeAllWindows: removing win=" + win);
-            win.mService.removeWindowLocked(win.mSession, win);
+            win.mService.removeWindowLocked(win);
         }
         windows.clear();
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a49dbde..2b88158 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3978,15 +3978,7 @@
                     + " for device owner");
         }
         synchronized (this) {
-            if (!allowedToSetDeviceOwnerOnDevice()) {
-                throw new IllegalStateException(
-                        "Trying to set device owner but device is already provisioned.");
-            }
-
-            if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) {
-                throw new IllegalStateException(
-                        "Trying to set device owner but device owner is already set.");
-            }
+            enforceCanSetDeviceOwner();
 
             // Shutting down backup manager service permanently.
             long ident = Binder.clearCallingIdentity();
@@ -4004,7 +3996,7 @@
                 // Device owner is not set and does not exist, set it.
                 mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName);
             } else {
-                // Device owner is not set but a profile owner exists, update Device owner state.
+                // Device owner state already exists, update it.
                 mDeviceOwner.setDeviceOwner(packageName, ownerName);
             }
             mDeviceOwner.writeOwnerFile();
@@ -4220,43 +4212,23 @@
         if (!mHasFeature) {
             return false;
         }
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
-
-        UserInfo info = mUserManager.getUserInfo(userHandle);
-        if (info == null) {
-            // User doesn't exist.
-            throw new IllegalArgumentException(
-                    "Attempted to set profile owner for invalid userId: " + userHandle);
-        }
-        if (info.isGuest()) {
-            throw new IllegalStateException("Cannot set a profile owner on a guest");
-        }
-
         if (who == null
                 || !DeviceOwner.isInstalledForUser(who.getPackageName(), userHandle)) {
             throw new IllegalArgumentException("Component " + who
                     + " not installed for userId:" + userHandle);
         }
         synchronized (this) {
-            // Only SYSTEM_UID can override the userSetupComplete
-            if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID
-                    && hasUserSetupCompleted(userHandle)) {
-                throw new IllegalStateException(
-                        "Trying to set profile owner but user is already set-up.");
-            }
-
+            enforceCanSetProfileOwner(userHandle);
             if (mDeviceOwner == null) {
                 // Device owner state does not exist, create it.
                 mDeviceOwner = DeviceOwner.createWithProfileOwner(who, ownerName,
                         userHandle);
-                mDeviceOwner.writeOwnerFile();
-                return true;
             } else {
-                // Device owner already exists, update it.
+                // Device owner state already exists, update it.
                 mDeviceOwner.setProfileOwner(who, ownerName, userHandle);
-                mDeviceOwner.writeOwnerFile();
-                return true;
             }
+            mDeviceOwner.writeOwnerFile();
+            return true;
         }
     }
 
@@ -4446,18 +4418,77 @@
     }
 
     /**
-     * Device owner can only be set on an unprovisioned device. However, if initiated via "adb",
-     * we also allow it if no accounts or additional users are present on the device.
+     * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
+     * permission.
+     * The profile owner can only be set before the user setup phase has completed,
+     * except for:
+     * - SYSTEM_UID
+     * - adb if there are not accounts.
      */
-    private boolean allowedToSetDeviceOwnerOnDevice() {
-        if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
-            return true;
+    private void enforceCanSetProfileOwner(int userHandle) {
+        UserInfo info = mUserManager.getUserInfo(userHandle);
+        if (info == null) {
+            // User doesn't exist.
+            throw new IllegalArgumentException(
+                    "Attempted to set profile owner for invalid userId: " + userHandle);
         }
+        if (info.isGuest()) {
+            throw new IllegalStateException("Cannot set a profile owner on a guest");
+        }
+        if (getProfileOwner(userHandle) != null) {
+            throw new IllegalStateException("Trying to set the profile owner, but profile owner "
+                    + "is already set.");
+        }
+        int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+            if (hasUserSetupCompleted(userHandle) &&
+                    AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) {
+                throw new IllegalStateException("Not allowed to set the profile owner because "
+                        + "there are already some accounts on the profile");
+            }
+            return;
+        }
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+        if (hasUserSetupCompleted(userHandle)
+                && UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
+            throw new IllegalStateException("Cannot set the profile owner on a user which is "
+                    + "already set-up");
+        }
+    }
 
-        int callingId = Binder.getCallingUid();
-        return (callingId == Process.SHELL_UID || callingId == Process.ROOT_UID)
-                && mUserManager.getUserCount() == 1
-                && AccountManager.get(mContext).getAccounts().length == 0;
+    /**
+     * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
+     * permission.
+     * The device owner can only be set before the setup phase of the primary user has completed,
+     * except for adb if no accounts or additional users are present on the device.
+     */
+    private void enforceCanSetDeviceOwner() {
+        if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) {
+            throw new IllegalStateException("Trying to set the device owner, but device owner "
+                    + "is already set.");
+        }
+        int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+            if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+                return;
+            }
+            if (mUserManager.getUserCount() > 1) {
+                throw new IllegalStateException("Not allowed to set the device owner because there "
+                        + "are already several users on the device");
+            }
+            if (AccountManager.get(mContext).getAccounts().length > 0) {
+                throw new IllegalStateException("Not allowed to set the device owner because there "
+                        + "are already some accounts on the device");
+            }
+            return;
+        }
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+        if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+            throw new IllegalStateException("Cannot set the device owner if the device is "
+                    + "already set-up");
+        }
     }
 
     private void enforceCrossUserPermission(int userHandle) {
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
index 25b8093..c0e1d19 100644
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -29,9 +29,9 @@
      */
     private final Inet4Address mSrcIp;
 
-    DhcpAckPacket(int transId, boolean broadcast, Inet4Address serverAddress,
+    DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
                   Inet4Address clientIp, byte[] clientMac) {
-        super(transId, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
+        super(transId, secs, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
         mBroadcast = broadcast;
         mSrcIp = serverAddress;
     }
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index ab56493..e1d1787 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -154,6 +154,7 @@
     private byte[] mHwAddr;
     private PacketSocketAddress mInterfaceBroadcastAddr;
     private int mTransactionId;
+    private long mTransactionStartMillis;
     private DhcpResults mDhcpLease;
     private long mDhcpLeaseExpiry;
     private DhcpResults mOffer;
@@ -264,8 +265,9 @@
         }
     }
 
-    private void initTransactionId() {
+    private void startNewTransaction() {
         mTransactionId = mRandom.nextInt();
+        mTransactionStartMillis = SystemClock.elapsedRealtime();
     }
 
     private boolean initSockets() {
@@ -344,6 +346,10 @@
         }
     }
 
+    private short getSecs() {
+        return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
+    }
+
     private boolean transmitPacket(ByteBuffer buf, String description, Inet4Address to) {
         try {
             if (to.equals(INADDR_BROADCAST)) {
@@ -362,7 +368,8 @@
 
     private boolean sendDiscoverPacket() {
         ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
-                DhcpPacket.ENCAP_L2, mTransactionId, mHwAddr, DO_UNICAST, REQUESTED_PARAMS);
+                DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
+                DO_UNICAST, REQUESTED_PARAMS);
         return transmitPacket(packet, "DHCPDISCOVER", INADDR_BROADCAST);
     }
 
@@ -373,7 +380,7 @@
         int encap = to.equals(INADDR_BROADCAST) ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
 
         ByteBuffer packet = DhcpPacket.buildRequestPacket(
-                encap, mTransactionId, clientAddress,
+                encap, mTransactionId, getSecs(), clientAddress,
                 DO_UNICAST, mHwAddr, requestedAddress,
                 serverAddress, REQUESTED_PARAMS, null);
         String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
@@ -669,7 +676,7 @@
         @Override
         public void enter() {
             super.enter();
-            initTransactionId();
+            startNewTransaction();
         }
 
         protected boolean sendPacket() {
@@ -776,7 +783,7 @@
         @Override
         public void enter() {
             super.enter();
-            initTransactionId();
+            startNewTransaction();
         }
 
         protected boolean sendPacket() {
diff --git a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
index 4a22b65..7ecdea7 100644
--- a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
+++ b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
@@ -26,10 +26,10 @@
     /**
      * Generates a DECLINE packet with the specified parameters.
      */
-    DhcpDeclinePacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+    DhcpDeclinePacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
                       Inet4Address nextIp, Inet4Address relayIp,
                       byte[] clientMac) {
-        super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
+        super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
     }
 
     public String toString() {
diff --git a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
index ed0fdc6..91e6bd6 100644
--- a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
@@ -26,8 +26,8 @@
     /**
      * Generates a DISCOVER packet with the specified parameters.
      */
-    DhcpDiscoverPacket(int transId, byte[] clientMac, boolean broadcast) {
-        super(transId, INADDR_ANY, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+    DhcpDiscoverPacket(int transId, short secs, byte[] clientMac, boolean broadcast) {
+        super(transId, secs, INADDR_ANY, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
     }
 
     public String toString() {
diff --git a/services/net/java/android/net/dhcp/DhcpInformPacket.java b/services/net/java/android/net/dhcp/DhcpInformPacket.java
index 2434fc9..7a83466 100644
--- a/services/net/java/android/net/dhcp/DhcpInformPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpInformPacket.java
@@ -26,10 +26,10 @@
     /**
      * Generates an INFORM packet with the specified parameters.
      */
-    DhcpInformPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+    DhcpInformPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
                      Inet4Address nextIp, Inet4Address relayIp,
                      byte[] clientMac) {
-        super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
+        super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
     }
 
     public String toString() {
diff --git a/services/net/java/android/net/dhcp/DhcpNakPacket.java b/services/net/java/android/net/dhcp/DhcpNakPacket.java
index 1390ea7..6458232 100644
--- a/services/net/java/android/net/dhcp/DhcpNakPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpNakPacket.java
@@ -26,10 +26,10 @@
     /**
      * Generates a NAK packet with the specified parameters.
      */
-    DhcpNakPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+    DhcpNakPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
                   Inet4Address nextIp, Inet4Address relayIp,
                   byte[] clientMac) {
-        super(transId, INADDR_ANY, INADDR_ANY, nextIp, relayIp,
+        super(transId, secs, INADDR_ANY, INADDR_ANY, nextIp, relayIp,
             clientMac, false);
     }
 
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
index b1f3bbd..af41708 100644
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -31,9 +31,9 @@
     /**
      * Generates a OFFER packet with the specified parameters.
      */
-    DhcpOfferPacket(int transId, boolean broadcast, Inet4Address serverAddress,
+    DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
                     Inet4Address clientIp, byte[] clientMac) {
-        super(transId, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+        super(transId, secs, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
         mSrcIp = serverAddress;
     }
 
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index a64ee6f..b923b1b 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -226,6 +226,11 @@
     protected final int mTransId;
 
     /**
+     * The seconds field in the BOOTP header. Per RFC, should be nonzero in client requests only.
+     */
+    protected final short mSecs;
+
+    /**
      * The IP address of the client host.  This address is typically
      * proposed by the client (from an earlier DHCP negotiation) or
      * supplied by the server.
@@ -258,10 +263,11 @@
      */
     abstract void finishPacket(ByteBuffer buffer);
 
-    protected DhcpPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+    protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
                          Inet4Address nextIp, Inet4Address relayIp,
                          byte[] clientMac, boolean broadcast) {
         mTransId = transId;
+        mSecs = secs;
         mClientIp = clientIp;
         mYourIp = yourIp;
         mNextIp = nextIp;
@@ -357,7 +363,7 @@
         buf.put((byte) mClientMac.length); // Hardware Address Length
         buf.put((byte) 0); // Hop Count
         buf.putInt(mTransId);  // Transaction ID
-        buf.putShort((short) 0); // Elapsed Seconds
+        buf.putShort(mSecs); // Elapsed Seconds
 
         if (broadcast) {
             buf.putShort((short) 0x8000); // Flags
@@ -652,6 +658,7 @@
     {
         // bootp parameters
         int transactionId;
+        short secs;
         Inet4Address clientIp;
         Inet4Address yourIp;
         Inet4Address nextIp;
@@ -759,7 +766,7 @@
         byte addrLen = packet.get();
         byte hops = packet.get();
         transactionId = packet.getInt();
-        short elapsed = packet.getShort();
+        secs = packet.getShort();
         short bootpFlags = packet.getShort();
         boolean broadcast = (bootpFlags & 0x8000) != 0;
         byte[] ipv4addr = new byte[4];
@@ -902,33 +909,33 @@
             case -1: return null;
             case DHCP_MESSAGE_TYPE_DISCOVER:
                 newPacket = new DhcpDiscoverPacket(
-                    transactionId, clientMac, broadcast);
+                    transactionId, secs, clientMac, broadcast);
                 break;
             case DHCP_MESSAGE_TYPE_OFFER:
                 newPacket = new DhcpOfferPacket(
-                    transactionId, broadcast, ipSrc, yourIp, clientMac);
+                    transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_REQUEST:
                 newPacket = new DhcpRequestPacket(
-                    transactionId, clientIp, clientMac, broadcast);
+                    transactionId, secs, clientIp, clientMac, broadcast);
                 break;
             case DHCP_MESSAGE_TYPE_DECLINE:
                 newPacket = new DhcpDeclinePacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    transactionId, secs, clientIp, yourIp, nextIp, relayIp,
                     clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_ACK:
                 newPacket = new DhcpAckPacket(
-                    transactionId, broadcast, ipSrc, yourIp, clientMac);
+                    transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_NAK:
                 newPacket = new DhcpNakPacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    transactionId, secs, clientIp, yourIp, nextIp, relayIp,
                     clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_INFORM:
                 newPacket = new DhcpInformPacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    transactionId, secs, clientIp, yourIp, nextIp, relayIp,
                     clientMac);
                 break;
             default:
@@ -1008,9 +1015,9 @@
      * parameters.
      */
     public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
-        byte[] clientMac, boolean broadcast, byte[] expectedParams) {
+        short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams) {
         DhcpPacket pkt = new DhcpDiscoverPacket(
-            transactionId, clientMac, broadcast);
+            transactionId, secs, clientMac, broadcast);
         pkt.mRequestedParams = expectedParams;
         return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
     }
@@ -1025,7 +1032,7 @@
         Inet4Address gateway, List<Inet4Address> dnsServers,
         Inet4Address dhcpServerIdentifier, String domainName) {
         DhcpPacket pkt = new DhcpOfferPacket(
-            transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
+            transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
         pkt.mGateway = gateway;
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
@@ -1045,7 +1052,7 @@
         Inet4Address gateway, List<Inet4Address> dnsServers,
         Inet4Address dhcpServerIdentifier, String domainName) {
         DhcpPacket pkt = new DhcpAckPacket(
-            transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
+            transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
         pkt.mGateway = gateway;
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
@@ -1061,7 +1068,7 @@
      */
     public static ByteBuffer buildNakPacket(int encap, int transactionId,
         Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac) {
-        DhcpPacket pkt = new DhcpNakPacket(transactionId, clientIpAddr,
+        DhcpPacket pkt = new DhcpNakPacket(transactionId, (short) 0, clientIpAddr,
             serverIpAddr, serverIpAddr, serverIpAddr, mac);
         pkt.mMessage = "requested address not available";
         pkt.mRequestedIp = clientIpAddr;
@@ -1072,10 +1079,10 @@
      * Builds a DHCP-REQUEST packet from the required specified parameters.
      */
     public static ByteBuffer buildRequestPacket(int encap,
-        int transactionId, Inet4Address clientIp, boolean broadcast,
+        int transactionId, short secs, Inet4Address clientIp, boolean broadcast,
         byte[] clientMac, Inet4Address requestedIpAddress,
         Inet4Address serverIdentifier, byte[] requestedParams, String hostName) {
-        DhcpPacket pkt = new DhcpRequestPacket(transactionId, clientIp,
+        DhcpPacket pkt = new DhcpRequestPacket(transactionId, secs, clientIp,
             clientMac, broadcast);
         pkt.mRequestedIp = requestedIpAddress;
         pkt.mServerIdentifier = serverIdentifier;
diff --git a/services/net/java/android/net/dhcp/DhcpRequestPacket.java b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
index 5d378b8..4f9aa01 100644
--- a/services/net/java/android/net/dhcp/DhcpRequestPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
@@ -28,9 +28,9 @@
     /**
      * Generates a REQUEST packet with the specified parameters.
      */
-    DhcpRequestPacket(int transId, Inet4Address clientIp, byte[] clientMac,
+    DhcpRequestPacket(int transId, short secs, Inet4Address clientIp, byte[] clientMac,
                       boolean broadcast) {
-        super(transId, clientIp, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+        super(transId, secs, clientIp, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
     }
 
     public String toString() {
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index 2658937..4f7c7ec 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -41,7 +41,8 @@
         private byte[] mDomainBytes, mVendorInfoBytes;
 
         public TestDhcpPacket(byte type, byte[] domainBytes, byte[] vendorInfoBytes) {
-            super(0xdeadbeef, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY, CLIENT_MAC, true);
+            super(0xdeadbeef, (short) 0, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY,
+                  CLIENT_MAC, true);
             mType = type;
             mDomainBytes = domainBytes;
             mVendorInfoBytes = vendorInfoBytes;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 3d54dfb..04984d3 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -17,7 +17,10 @@
 package com.android.server.usage;
 
 import android.Manifest;
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
@@ -25,11 +28,13 @@
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
+import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
@@ -82,6 +87,7 @@
     static final int MSG_FLUSH_TO_DISK = 1;
     static final int MSG_REMOVE_USER = 2;
     static final int MSG_INFORM_LISTENERS = 3;
+    static final int MSG_RESET_LAST_TIMESTAMP = 4;
 
     private final Object mLock = new Object();
     Handler mHandler;
@@ -279,6 +285,29 @@
     }
 
     /**
+     * Forces the app's timestamp to reflect idle or active. If idle, then it rolls back the
+     * last used timestamp to a point in time thats behind the threshold for idle.
+     */
+    void resetLastTimestamp(String packageName, int userId, boolean idle) {
+        synchronized (mLock) {
+            final long timeNow = checkAndGetTimeLocked();
+            final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0);
+
+            final UserUsageStatsService service =
+                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+            final long lastUsed = service.getLastPackageAccessTime(packageName);
+            final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
+            service.setLastTimestamp(packageName, lastTimestamp);
+            // Inform listeners if necessary
+            if (previouslyIdle != idle) {
+                // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+                        /* idle = */ idle ? 1 : 0, packageName));
+            }
+        }
+    }
+
+    /**
      * Called by the Binder stub.
      */
     void flushToDisk() {
@@ -384,13 +413,39 @@
     }
 
     boolean isAppIdle(String packageName, int userId) {
+        if (packageName == null) return false;
         if (SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) {
             return false;
         }
+        if (isActiveDeviceAdmin(packageName, userId)) {
+            return false;
+        }
+
         final long lastUsed = getLastPackageAccessTime(packageName, userId);
         return hasPassedIdleDuration(lastUsed);
     }
 
+    void setAppIdle(String packageName, boolean idle, int userId) {
+        if (packageName == null) return;
+
+        mHandler.obtainMessage(MSG_RESET_LAST_TIMESTAMP, userId, idle ? 1 : 0, packageName)
+                .sendToTarget();
+    }
+
+    private boolean isActiveDeviceAdmin(String packageName, int userId) {
+        DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+        if (dpm == null) return false;
+        List<ComponentName> components = dpm.getActiveAdminsAsUser(userId);
+        if (components == null) return false;
+        final int size = components.size();
+        for (int i = 0; i < size; i++) {
+            if (components.get(i).getPackageName().equals(packageName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void informListeners(String packageName, int userId, boolean isIdle) {
         for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
             listener.onAppIdleStateChanged(packageName, userId, isIdle);
@@ -459,6 +514,10 @@
                     informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
                     break;
 
+                case MSG_RESET_LAST_TIMESTAMP:
+                    resetLastTimestamp((String) msg.obj, msg.arg1, msg.arg2 == 1);
+                    break;
+
                 default:
                     super.handleMessage(msg);
                     break;
@@ -566,6 +625,46 @@
         }
 
         @Override
+        public boolean isAppIdle(String packageName, int userId) {
+            try {
+                userId = ActivityManagerNative.getDefault().handleIncomingUser(Binder.getCallingPid(),
+                        Binder.getCallingUid(), userId, false, true, "isAppIdle", null);
+            } catch (RemoteException re) {
+                return false;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return UsageStatsService.this.isAppIdle(packageName, userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setAppIdle(String packageName, boolean idle, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            try {
+                userId = ActivityManagerNative.getDefault().handleIncomingUser(
+                        Binder.getCallingPid(), callingUid, userId, false, true,
+                        "setAppIdle", null);
+            } catch (RemoteException re) {
+                return;
+            }
+            getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
+                    "No permission to change app idle state");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                PackageInfo pi = AppGlobals.getPackageManager()
+                        .getPackageInfo(packageName, 0, userId);
+                if (pi == null) return;
+                UsageStatsService.this.setAppIdle(packageName, idle, userId);
+            } catch (RemoteException re) {
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 0a9481a..d94759d 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -211,6 +211,17 @@
         notifyStatsChanged();
     }
 
+    /**
+     * Sets the last timestamp for each of the intervals.
+     * @param lastTimestamp
+     */
+    void setLastTimestamp(String packageName, long lastTimestamp) {
+        for (IntervalStats stats : mCurrentStats) {
+            stats.update(packageName, lastTimestamp, UsageEvents.Event.NONE);
+        }
+        notifyStatsChanged();
+    }
+
     private static final StatCombiner<UsageStats> sUsageStatsCombiner =
             new StatCombiner<UsageStats>() {
                 @Override
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 8f0c6c8..daccf95 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -142,8 +142,10 @@
 
         // add existing alsa devices
         File[] files = new File(ALSA_DIRECTORY).listFiles();
-        for (int i = 0; i < files.length; i++) {
-            alsaFileAdded(files[i].getName());
+        if (files != null) {
+            for (int i = 0; i < files.length; i++) {
+                alsaFileAdded(files[i].getName());
+            }
         }
     }
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2a30384..d92c0c7 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -933,7 +933,8 @@
                     Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
         }
 
-        boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
+        boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
+                !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
         if (videoCallChanged) {
             mVideoCall = parcelableCall.getVideoCall();
         }
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index bab60fe..0424548 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -50,6 +50,7 @@
                 Conference conference, int connectionCapabilities) {}
         public void onVideoStateChanged(Conference c, int videoState) { }
         public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
+        public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
     }
 
     private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -67,6 +68,7 @@
     private int mConnectionCapabilities;
     private String mDisconnectMessage;
     private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
+    private StatusHints mStatusHints;
 
     private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
         @Override
@@ -535,4 +537,23 @@
                 getVideoProvider(),
                 super.toString());
     }
+
+    /**
+     * Sets the label and icon status to display in the InCall UI.
+     *
+     * @param statusHints The status label and icon to set.
+     */
+    public final void setStatusHints(StatusHints statusHints) {
+        mStatusHints = statusHints;
+        for (Listener l : mListeners) {
+            l.onStatusHintsChanged(this, statusHints);
+        }
+    }
+
+    /**
+     * @return The status hints for this conference.
+     */
+    public final StatusHints getStatusHints() {
+        return mStatusHints;
+    }
 }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index e79584f..cd10050 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -28,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -402,7 +403,7 @@
          */
         public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
 
-        private static final int MSG_SET_VIDEO_CALLBACK = 1;
+        private static final int MSG_ADD_VIDEO_CALLBACK = 1;
         private static final int MSG_SET_CAMERA = 2;
         private static final int MSG_SET_PREVIEW_SURFACE = 3;
         private static final int MSG_SET_DISPLAY_SURFACE = 4;
@@ -413,11 +414,16 @@
         private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
         private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
         private static final int MSG_SET_PAUSE_IMAGE = 11;
+        private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
 
         private final VideoProvider.VideoProviderHandler
                 mMessageHandler = new VideoProvider.VideoProviderHandler();
         private final VideoProvider.VideoProviderBinder mBinder;
-        private IVideoCallback mVideoCallback;
+
+        /**
+         * Stores a list of the video callbacks, keyed by IBinder.
+         */
+        private HashMap<IBinder, IVideoCallback> mVideoCallbacks = new HashMap<>();
 
         /**
          * Default handler used to consolidate binder method calls onto a single thread.
@@ -426,9 +432,29 @@
             @Override
             public void handleMessage(Message msg) {
                 switch (msg.what) {
-                    case MSG_SET_VIDEO_CALLBACK:
-                        mVideoCallback = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
+                    case MSG_ADD_VIDEO_CALLBACK: {
+                        IBinder binder = (IBinder) msg.obj;
+                        IVideoCallback callback = IVideoCallback.Stub
+                                .asInterface((IBinder) msg.obj);
+                        if (mVideoCallbacks.containsKey(binder)) {
+                            Log.i(this, "addVideoProvider - skipped; already present.");
+                            break;
+                        }
+                        mVideoCallbacks.put(binder, callback);
+                        Log.i(this, "addVideoProvider  "+ mVideoCallbacks.size());
                         break;
+                    }
+                    case MSG_REMOVE_VIDEO_CALLBACK: {
+                        IBinder binder = (IBinder) msg.obj;
+                        IVideoCallback callback = IVideoCallback.Stub
+                                .asInterface((IBinder) msg.obj);
+                        if (!mVideoCallbacks.containsKey(binder)) {
+                            Log.i(this, "removeVideoProvider - skipped; not present.");
+                            break;
+                        }
+                        mVideoCallbacks.remove(binder);
+                        break;
+                    }
                     case MSG_SET_CAMERA:
                         onSetCamera((String) msg.obj);
                         break;
@@ -469,9 +495,14 @@
          * IVideoProvider stub implementation.
          */
         private final class VideoProviderBinder extends IVideoProvider.Stub {
-            public void setVideoCallback(IBinder videoCallbackBinder) {
+            public void addVideoCallback(IBinder videoCallbackBinder) {
                 mMessageHandler.obtainMessage(
-                        MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+                        MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+            }
+
+            public void removeVideoCallback(IBinder videoCallbackBinder) {
+                mMessageHandler.obtainMessage(
+                        MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
             }
 
             public void setCamera(String cameraId) {
@@ -609,21 +640,23 @@
         public abstract void onSetPauseImage(String uri);
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param videoProfile The requested video connection profile.
          */
         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.receiveSessionModifyRequest(videoProfile);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.receiveSessionModifyRequest(videoProfile);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param status Status of the session modify request.  Valid values are
          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
@@ -634,17 +667,19 @@
          */
         public void receiveSessionModifyResponse(int status,
                 VideoProfile requestedProfile, VideoProfile responseProfile) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.receiveSessionModifyResponse(
-                            status, requestedProfile, responseProfile);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.receiveSessionModifyResponse(status, requestedProfile,
+                                responseProfile);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
          * {@link VideoProvider#SESSION_EVENT_RX_RESUME},
@@ -654,66 +689,76 @@
          * @param event The event.
          */
         public void handleCallSessionEvent(int event) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.handleCallSessionEvent(event);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.handleCallSessionEvent(event);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param width  The updated peer video width.
          * @param height The updated peer video height.
          */
         public void changePeerDimensions(int width, int height) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changePeerDimensions(width, height);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changePeerDimensions(width, height);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param dataUsage The updated data usage.
          */
         public void changeCallDataUsage(long dataUsage) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changeCallDataUsage(dataUsage);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeCallDataUsage(dataUsage);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param cameraCapabilities The changed camera capabilities.
          */
         public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changeCameraCapabilities(cameraCapabilities);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeCameraCapabilities(cameraCapabilities);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param videoQuality The updated video quality.
          */
         public void changeVideoQuality(int videoQuality) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changeVideoQuality(videoQuality);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeVideoQuality(videoQuality);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 9812815..c039acf 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -427,6 +427,12 @@
                     videoProvider);
             mAdapter.setVideoProvider(id, videoProvider);
         }
+
+        @Override
+        public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {
+            String id = mIdByConference.get(conference);
+            mAdapter.setStatusHints(id, statusHints);
+        }
     };
 
     private final Connection.Listener mConnectionListener = new Connection.Listener() {
@@ -903,8 +909,9 @@
                     conference.getVideoProvider() == null ?
                             null : conference.getVideoProvider().getInterface(),
                     conference.getVideoState(),
-                    conference.getConnectTimeMillis()
-                    );
+                    conference.getConnectTimeMillis(),
+                    conference.getStatusHints());
+
             mAdapter.addConferenceCall(id, parcelableConference);
             mAdapter.setVideoProvider(id, conference.getVideoProvider());
             mAdapter.setVideoState(id, conference.getVideoState());
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index eef72fb..bf8fac6 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -87,15 +87,15 @@
         }
 
         // No user-set dialer found, fallback to system dialer
-        ComponentName systemDialer = getTelecomManager(context).getDefaultPhoneApp();
+        String systemDialer = getTelecomManager(context).getSystemDialerPackage();
 
-        if (systemDialer == null) {
+        if (TextUtils.isEmpty(systemDialer)) {
             // No system dialer configured at build time
             return null;
         }
 
         // Verify that the system dialer has not been disabled.
-        return getComponentName(componentNames, systemDialer.getPackageName());
+        return getComponentName(componentNames, systemDialer);
     }
 
     /**
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 73bcd0c..3a7faf6 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -130,8 +130,10 @@
 
     /**
      * Returns a short label which explains the reason for the disconnect cause and is for display
-     * in the user interface. The {@link ConnectionService } is responsible for providing and
-     * localizing this label. If there is no string provided, returns null.
+     * in the user interface. If not null, it is expected that the In-Call UI should display this
+     * text where it would normally display the call state ("Dialing", "Disconnected") and is
+     * therefore expected to be relatively small. The {@link ConnectionService } is responsible for
+     * providing and localizing this label. If there is no string provided, returns null.
      *
      * @return The disconnect label.
      */
@@ -141,8 +143,11 @@
 
     /**
      * Returns a description which explains the reason for the disconnect cause and is for display
-     * in the user interface. The {@link ConnectionService } is responsible for providing and
-     * localizing this message. If there is no string provided, returns null.
+     * in the user interface. This optional text is generally a longer and more descriptive version
+     * of {@link #getLabel}, however it can exist even if {@link #getLabel} is empty. The In-Call UI
+     * should display this relatively prominently; the traditional implementation displays this as
+     * an alert dialog. The {@link ConnectionService} is responsible for providing and localizing
+     * this message. If there is no string provided, returns null.
      *
      * @return The disconnect description.
      */
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 7cbc0fc..caa1731 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -370,6 +370,11 @@
         public abstract void registerCallback(VideoCall.Callback callback);
 
         /**
+         * Clears the video call listener set via {@link #registerCallback(callback)}.
+         */
+        public abstract void unregisterCallback();
+
+        /**
          * Sets the camera to be used for video recording in a video call.
          *
          * @param cameraId The id of the camera.
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index c5c3d11..1a30910 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -46,6 +46,7 @@
     private final int mCallerDisplayNamePresentation;
     private final GatewayInfo mGatewayInfo;
     private final PhoneAccountHandle mAccountHandle;
+    private final boolean mIsVideoCallProviderChanged;
     private final IVideoProvider mVideoCallProvider;
     private InCallService.VideoCall mVideoCall;
     private final String mParentCallId;
@@ -69,6 +70,7 @@
             int callerDisplayNamePresentation,
             GatewayInfo gatewayInfo,
             PhoneAccountHandle accountHandle,
+            boolean isVideoCallProviderChanged,
             IVideoProvider videoCallProvider,
             String parentCallId,
             List<String> childCallIds,
@@ -89,6 +91,7 @@
         mCallerDisplayNamePresentation = callerDisplayNamePresentation;
         mGatewayInfo = gatewayInfo;
         mAccountHandle = accountHandle;
+        mIsVideoCallProviderChanged = isVideoCallProviderChanged;
         mVideoCallProvider = videoCallProvider;
         mParentCallId = parentCallId;
         mChildCallIds = childCallIds;
@@ -232,6 +235,18 @@
         return mExtras;
     }
 
+    /**
+     * Indicates to the receiver of the {@link ParcelableCall} whether a change has occurred in the
+     * {@link android.telecom.InCallService.VideoCall} associated with this call.  Since
+     * {@link #getVideoCall()} creates a new {@link VideoCallImpl}, it is useful to know whether
+     * the provider has changed (which can influence whether it is accessed).
+     *
+     * @return {@code true} if the video call changed, {@code false} otherwise.
+     */
+    public boolean isVideoCallProviderChanged() {
+        return mIsVideoCallProviderChanged;
+    }
+
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
             new Parcelable.Creator<ParcelableCall> () {
@@ -252,6 +267,7 @@
             int callerDisplayNamePresentation = source.readInt();
             GatewayInfo gatewayInfo = source.readParcelable(classLoader);
             PhoneAccountHandle accountHandle = source.readParcelable(classLoader);
+            boolean isVideoCallProviderChanged = source.readByte() == 1;
             IVideoProvider videoCallProvider =
                     IVideoProvider.Stub.asInterface(source.readStrongBinder());
             String parentCallId = source.readString();
@@ -276,6 +292,7 @@
                     callerDisplayNamePresentation,
                     gatewayInfo,
                     accountHandle,
+                    isVideoCallProviderChanged,
                     videoCallProvider,
                     parentCallId,
                     childCallIds,
@@ -313,6 +330,7 @@
         destination.writeInt(mCallerDisplayNamePresentation);
         destination.writeParcelable(mGatewayInfo, 0);
         destination.writeParcelable(mAccountHandle, 0);
+        destination.writeByte((byte) (mIsVideoCallProviderChanged ? 1 : 0));
         destination.writeStrongBinder(
                 mVideoCallProvider != null ? mVideoCallProvider.asBinder() : null);
         destination.writeString(mParentCallId);
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index ab82549..e54e79d 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -34,25 +34,10 @@
     private int mState;
     private int mConnectionCapabilities;
     private List<String> mConnectionIds;
-    private long mConnectTimeMillis;
+    private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
     private final IVideoProvider mVideoProvider;
     private final int mVideoState;
-
-    public ParcelableConference(
-            PhoneAccountHandle phoneAccount,
-            int state,
-            int connectionCapabilities,
-            List<String> connectionIds,
-            IVideoProvider videoProvider,
-            int videoState) {
-        mPhoneAccount = phoneAccount;
-        mState = state;
-        mConnectionCapabilities = connectionCapabilities;
-        mConnectionIds = connectionIds;
-        mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
-        mVideoProvider = videoProvider;
-        mVideoState = videoState;
-    }
+    private StatusHints mStatusHints;
 
     public ParcelableConference(
             PhoneAccountHandle phoneAccount,
@@ -61,9 +46,17 @@
             List<String> connectionIds,
             IVideoProvider videoProvider,
             int videoState,
-            long connectTimeMillis) {
-        this(phoneAccount, state, connectionCapabilities, connectionIds, videoProvider, videoState);
+            long connectTimeMillis,
+            StatusHints statusHints) {
+        mPhoneAccount = phoneAccount;
+        mState = state;
+        mConnectionCapabilities = connectionCapabilities;
+        mConnectionIds = connectionIds;
+        mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+        mVideoProvider = videoProvider;
+        mVideoState = videoState;
         mConnectTimeMillis = connectTimeMillis;
+        mStatusHints = statusHints;
     }
 
     @Override
@@ -113,6 +106,10 @@
         return mVideoState;
     }
 
+    public StatusHints getStatusHints() {
+        return mStatusHints;
+    }
+
     public static final Parcelable.Creator<ParcelableConference> CREATOR =
             new Parcelable.Creator<ParcelableConference> () {
         @Override
@@ -124,13 +121,14 @@
             List<String> connectionIds = new ArrayList<>(2);
             source.readList(connectionIds, classLoader);
             long connectTimeMillis = source.readLong();
+            StatusHints statusHints = source.readParcelable(classLoader);
 
             IVideoProvider videoCallProvider =
                     IVideoProvider.Stub.asInterface(source.readStrongBinder());
             int videoState = source.readInt();
 
             return new ParcelableConference(phoneAccount, state, capabilities, connectionIds,
-                    videoCallProvider, videoState);
+                    videoCallProvider, videoState, connectTimeMillis, statusHints);
         }
 
         @Override
@@ -156,5 +154,6 @@
         destination.writeStrongBinder(
                 mVideoProvider != null ? mVideoProvider.asBinder() : null);
         destination.writeInt(mVideoState);
+        destination.writeParcelable(mStatusHints, 0);
     }
 }
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index c1c1129..3d9acda 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -122,6 +122,11 @@
     final void internalRemoveCall(Call call) {
         mCallByTelecomCallId.remove(call.internalGetCallId());
         mCalls.remove(call);
+
+        InCallService.VideoCall videoCall = call.getVideoCall();
+        if (videoCall != null) {
+            videoCall.unregisterCallback();
+        }
         fireCallRemoved(call);
     }
 
@@ -167,6 +172,10 @@
      */
     final void destroy() {
         for (Call call : mCalls) {
+            InCallService.VideoCall videoCall = call.getVideoCall();
+            if (videoCall != null) {
+                videoCall.unregisterCallback();
+            }
             if (call.getState() != Call.STATE_DISCONNECTED) {
                 call.internalSetDisconnected();
             }
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 4c423f2..4ecfd50 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -301,7 +301,7 @@
         public VideoProvider(IVideoProvider videoProviderBinder) {
             mVideoProviderBinder = videoProviderBinder;
             try {
-                mVideoProviderBinder.setVideoCallback(mVideoCallbackServant.getStub().asBinder());
+                mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
             } catch (RemoteException e) {
             }
         }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index fd95327..8d6bda8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -110,6 +110,28 @@
             "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
 
     /**
+     * Activity action: Shows a dialog asking the user whether or not they want to replace the
+     * current default Dialer with the one specified in
+     * {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME}.
+     *
+     * Usage example:
+     * <pre>
+     * Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
+     * intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
+     *         getActivity().getPackageName());
+     * startActivity(intent);
+     * </pre>
+     */
+    public static final String ACTION_CHANGE_DEFAULT_DIALER =
+            "android.telecom.action.CHANGE_DEFAULT_DIALER";
+
+    /**
+     * Extra value used to provide the package name for {@link #ACTION_CHANGE_DEFAULT_DIALER}.
+     */
+    public static final String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME =
+            "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+
+    /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
      * determines whether the speakerphone should be automatically turned on for an outgoing call.
      */
@@ -689,7 +711,10 @@
         }
     }
 
+
     /**
+     * @deprecated - Use {@link TelecomManager#getDefaultDialerPackage} to directly access
+     *         the default dialer's package name instead.
      * @hide
      */
     @SystemApi
@@ -705,6 +730,40 @@
     }
 
     /**
+     * Used to determine the currently selected default dialer package.
+     *
+     * @return package name for the default dialer package or null if no package has been
+     *         selected as the default dialer.
+     */
+    public String getDefaultDialerPackage() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().getDefaultDialerPackage();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
+        }
+        return null;
+    }
+
+    /**
+     * Used to determine the dialer package that is preloaded on the system partition.
+     *
+     * @return package name for the system dialer package or null if no system dialer is preloaded.
+     * @hide
+     */
+    public String getSystemDialerPackage() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().getSystemDialerPackage();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException attempting to get the system dialer package name.", e);
+        }
+        return null;
+    }
+
+    /**
      * Return whether a given phone number is the configured voicemail number for a
      * particular phone account.
      *
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index 7bef688..3779d1a 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -166,7 +166,7 @@
         mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
 
         mBinder = new VideoCallListenerBinder();
-        mVideoProvider.setVideoCallback(mBinder);
+        mVideoProvider.addVideoCallback(mBinder);
     }
 
     /** {@inheritDoc} */
@@ -175,6 +175,15 @@
     }
 
     /** {@inheritDoc} */
+    public void unregisterCallback() {
+        mCallback = null;
+        try {
+            mVideoProvider.removeVideoCallback(mBinder);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /** {@inheritDoc} */
     public void setCamera(String cameraId) {
         try {
             mVideoProvider.setCamera(cameraId);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 45b2482..49f2aad 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -137,6 +137,16 @@
      */
     ComponentName getDefaultPhoneApp();
 
+    /**
+     * @see TelecomServiceImpl#getDefaultDialerPackage
+     */
+    String getDefaultDialerPackage();
+
+    /**
+     * @see TelecomServiceImpl#getSystemDialerPackage
+     */
+    String getSystemDialerPackage();
+
     //
     // Internal system apis relating to call management.
     //
diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
index e96d9d3..bff3865 100644
--- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
@@ -25,7 +25,9 @@
  * @hide
  */
 oneway interface IVideoProvider {
-    void setVideoCallback(IBinder videoCallbackBinder);
+    void addVideoCallback(IBinder videoCallbackBinder);
+
+    void removeVideoCallback(IBinder videoCallbackBinder);
 
     void setCamera(String cameraId);
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7d1a2fa..831a194 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -208,13 +208,13 @@
     }
 
     /**
-     * Returns a bundle with the default value for every supported configuration variable.
+     * Returns a new bundle with the default value for every supported configuration variable.
      *
      * @hide
      */
     @SystemApi
     public static Bundle getDefaultConfig() {
-        return sDefaults;
+        return new Bundle(sDefaults);
     }
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index 84d1c545..0443c3e 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -115,4 +115,12 @@
      * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
      */
     void callSessionTtyModeReceived(in IImsCallSession session, in int mode);
+
+    /**
+     * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+     *
+     * @param session The call session.
+     * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+     */
+    void callSessionMultipartyStateChanged(in IImsCallSession session, in boolean isMultiParty);
 }
diff --git a/tools/obbtool/pbkdf2gen.cpp b/tools/obbtool/pbkdf2gen.cpp
index 98d67c0..f1d8d04 100644
--- a/tools/obbtool/pbkdf2gen.cpp
+++ b/tools/obbtool/pbkdf2gen.cpp
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index b156d0c..d2fb0dd 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -808,7 +808,7 @@
         if (mRttCapabilities == null) {
             if(getRttCapabilities() == null) {
                 Log.e(TAG, "Can not get RTT capabilities");
-                //throw new IllegalStateException("RTT chip is not working");
+                throw new IllegalStateException("RTT chip is not working");
             }
         }
 
@@ -866,6 +866,15 @@
         return true;
     }
 
+    /**
+     * Request to start an RTT ranging
+     *
+     * @param params  -- RTT request Parameters
+     * @param listener -- Call back to inform RTT result
+     * @exception throw IllegalArgumentException when params are illegal
+     *            throw IllegalStateException when RttCapabilities do not exist
+     */
+
     public void startRanging(RttParams[] params, RttListener listener) {
         int index  = 0;
         for(RttParams rttParam : params) {
@@ -874,9 +883,9 @@
             }
             index++;
         }
-
         validateChannel();
         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
+        Log.i(TAG, "Send RTT request to RTT Service");
         sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
                 0, putListener(listener), parcelableParams);
     }
@@ -1024,6 +1033,7 @@
         }
         @Override
         public void handleMessage(Message msg) {
+            Log.i(TAG, "RTT manager get message: " + msg.what);
             switch (msg.what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -1049,10 +1059,10 @@
 
             Object listener = getListener(msg.arg2);
             if (listener == null) {
-                if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
+                Log.e(TAG, "invalid listener key = " + msg.arg2 );
                 return;
             } else {
-                if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
+                Log.i(TAG, "listener key = " + msg.arg2);
             }
 
             switch (msg.what) {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index c6f2991..b731316 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -243,6 +243,7 @@
      * The band which AP resides on
      * 0-2G  1-5G
      * By default, 2G is chosen
+     * @hide
      */
     public int apBand = 0;
 
@@ -251,6 +252,7 @@
      * 2G  1-11
      * 5G  36,40,44,48,149,153,157,161,165
      * 0 - find a random available channel according to the apBand
+     * @hide
      */
     public int apChannel = 0;
 
@@ -953,7 +955,7 @@
         if (!TextUtils.isEmpty(FQDN)) {
             /* this is passpoint configuration; it must not have an SSID */
             if (!TextUtils.isEmpty(SSID)) {
-                return "no SSID";
+                return "SSID not expected for Passpoint: '" + SSID + "'";
             }
             /* this is passpoint configuration; it must have a providerFriendlyName */
             if (TextUtils.isEmpty(providerFriendlyName)) {
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 6917971..3525ec2 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -56,6 +56,8 @@
     /** @hide */
     public static final String ALTSUBJECT_MATCH_KEY = "altsubject_match";
     /** @hide */
+    public static final String DOM_SUFFIX_MATCH_KEY = "domain_suffix_match";
+    /** @hide */
     public static final String OPP_KEY_CACHING     = "proactive_key_caching";
     /**
      * String representing the keystore OpenSSL ENGINE's ID.
@@ -577,6 +579,36 @@
     }
 
     /**
+     * Set the domain_suffix_match directive on wpa_supplicant. This is the parameter to use
+     * for Hotspot 2.0 defined matching of AAA server certs per WFA HS2.0 spec, section 7.3.3.2,
+     * second paragraph.
+     *
+     * From wpa_supplicant documentation:
+     * Constraint for server domain name. If set, this FQDN is used as a suffix match requirement
+     * for the AAAserver certificate in SubjectAltName dNSName element(s). If a matching dNSName is
+     * found, this constraint is met. If no dNSName values are present, this constraint is matched
+     * against SubjectName CN using same suffix match comparison.
+     * Suffix match here means that the host/domain name is compared one label at a time starting
+     * from the top-level domain and all the labels in domain_suffix_match shall be included in the
+     * certificate. The certificate may include additional sub-level labels in addition to the
+     * required labels.
+     * For example, domain_suffix_match=example.com would match test.example.com but would not
+     * match test-example.com.
+     * @param domain The domain value
+     */
+    public void setDomainSuffixMatch(String domain) {
+        setFieldValue(DOM_SUFFIX_MATCH_KEY, domain);
+    }
+
+    /**
+     * Get the domain_suffix_match value. See setDomSuffixMatch.
+     * @return The domain value.
+     */
+    public String getDomainSubjectMatch() {
+        return getFieldValue(DOM_SUFFIX_MATCH_KEY, "");
+    }
+
+    /**
      * Set realm for passpoint credential
      * @param realm the realm
      */