diff options
163 files changed, 3207 insertions, 1601 deletions
diff --git a/api/current.txt b/api/current.txt index fee81f5cc630..578f80819263 100644 --- a/api/current.txt +++ b/api/current.txt @@ -505,7 +505,6 @@ package android { 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 @@ package android { 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 @@ package android.animation { 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 @@ package android.animation { 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 @@ package android.app.usage { } 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media.tv { 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 @@ package android.net { 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 @@ package android.net { 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 @@ package android.net { 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 @@ package android.net.wifi { 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 @@ package android.net.wifi { 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 @@ package android.net.wifi { 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 @@ package android.os { 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 @@ package android.os { 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.sax { 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 @@ package android.security { 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 @@ package android.security { 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 @@ package android.security { 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 @@ package android.service.notification { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 137bb19c4a60..2ea68c613249 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -578,7 +578,6 @@ package android { 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 @@ package android { 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 @@ package android.animation { 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 @@ package android.animation { 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 @@ package android.app.usage { } 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media { 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 @@ package android.media.tv { 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 @@ package android.net { 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 @@ package android.net { 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 @@ package android.net { 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 @@ package android.net.wifi { 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 @@ package android.net.wifi { 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 @@ package android.net.wifi { 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 @@ package android.os { 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 @@ package android.os { 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.sax { 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 @@ package android.security { 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 @@ package android.security { 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 @@ package android.security { 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 @@ package android.service.notification { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 @@ package android.telecom { 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 8ba2a5ac6b55..219d35bd50cb 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 @@ public class Am extends BaseCommand { " 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 @@ public class Am extends BaseCommand { "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 @@ public class Am extends BaseCommand { 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 @@ public class Am extends BaseCommand { } } + 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 b84b1e202888..39de1dc7c7a5 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.FeatureInfo; 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 @@ public final class Pm { 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 final class Pm { } } - public int runMove() { + public int runMovePackage() { final String packageName = nextArg(); String volumeUuid = nextArg(); if ("internal".equals(volumeUuid)) { @@ -1313,6 +1316,33 @@ public final class Pm { } } + 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 @@ public final class Pm { 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 02a329d3ec06..da4870901db9 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 @@ import java.util.ArrayList; * 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 @@ public abstract class Animator implements Cloneable { 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 class Animator implements Cloneable { 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 e47d017f937c..427ecce6fec4 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -70,13 +70,6 @@ public class AnimatorInflater { 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 @@ public class AnimatorInflater { 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 @@ public class AnimatorInflater { 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 f6ad847c5f33..6503d892c027 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -519,7 +519,6 @@ public final class AnimatorSet extends Animator { 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 292507bcc253..a455f8b3a8e4 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 @@ public class ValueAnimator extends Animator { } 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 ab5f81189c2d..9bad9bb7a6fc 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -255,23 +255,18 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } - static final class ProviderClientRecord { + final class ProviderClientRecord { final String[] mNames; final IContentProvider mProvider; final ContentProvider mLocalProvider; @@ -4716,74 +4711,23 @@ public final class ActivityThread { 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 class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 10f59602b93f..16a2430a93d7 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1556,6 +1556,7 @@ final class ApplicationPackageManager extends PackageManager { } } + @Override public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() { final StorageManager storage = mContext.getSystemService(StorageManager.class); final String volumeUuid = storage.getPrimaryStorageUuid(); @@ -1568,6 +1569,7 @@ final class ApplicationPackageManager extends PackageManager { } } + @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 e275df045b98..ac8d5d89de61 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -67,6 +67,8 @@ interface INotificationManager 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 56cd53e20049..ebb3c43fc759 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 @@ public class KeyguardManager { * * @see #reenableKeyguard() */ + @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD) public void disableKeyguard() { try { mWM.disableKeyguard(mToken, mTag); @@ -132,6 +135,7 @@ public class KeyguardManager { * * @see #disableKeyguard() */ + @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard() { try { mWM.reenableKeyguard(mToken); @@ -302,6 +306,7 @@ public class KeyguardManager { * 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 a20aa6687069..47133d417912 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2651,14 +2651,12 @@ public class DevicePolicyManager { /** * @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 @@ public class DevicePolicyManager { /** * @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 @@ public class DevicePolicyManager { /** * @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 4ed148911a43..23659e389afc 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -30,4 +30,6 @@ interface IUsageStatsManager { 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 bc6099a7d5e3..8a01d663055c 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -19,6 +19,7 @@ package android.app.usage; 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 @@ public final class UsageStatsManager { } 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 79e560ff9f4b..3044a9494a92 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -130,7 +130,7 @@ public final class BluetoothAdapter { * {@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 final class BluetoothAdapter { 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 cdb24f40e2de..01778b3dc40e 100644 --- a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -123,4 +123,7 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { 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 f6001bfffedd..a3eceb5efd9d 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -100,7 +100,6 @@ interface IBluetooth 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 4ca57f8eccab..72abeaf699c1 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -103,4 +103,5 @@ interface IBluetoothGatt { 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 91e62ea65c73..cbba9f021423 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -67,6 +67,7 @@ oneway interface IBluetoothGattCallback { 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 307895129120..687bd5db099e 100644 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java @@ -381,6 +381,18 @@ public final class BluetoothLeScanner { } }); } + + @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 31e6e254a951..0cf8df130cd4 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -65,6 +65,12 @@ import java.util.List; 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 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @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 @@ public abstract class CameraCaptureSession implements AutoCloseable { * 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 35727e871e40..19d17b136bff 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -158,6 +158,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> 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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(); mSurfaceSet = new HashSet<Surface>(); mIsReprocess = false; + mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE; } /** @@ -182,6 +186,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(source.mSettings); mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); mIsReprocess = source.mIsReprocess; + mReprocessibleSessionId = source.mReprocessibleSessionId; mUserTag = source.mUserTag; } @@ -189,11 +194,36 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * 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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } /** + * 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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> && 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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } mIsReprocess = (in.readInt() == 0) ? false : true; + mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE; } @Override @@ -397,10 +446,23 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * 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 6f7dd78952c2..fb3c098a8f29 100644 --- a/core/java/android/hardware/camera2/TotalCaptureResult.java +++ b/core/java/android/hardware/camera2/TotalCaptureResult.java @@ -50,6 +50,7 @@ import java.util.List; 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 @@ public final class TotalCaptureResult extends CaptureResult { * @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 @@ public final class TotalCaptureResult extends CaptureResult { } else { mPartialResults = partials; } + + mSessionId = sessionId; } /** @@ -78,6 +81,7 @@ public final class TotalCaptureResult extends CaptureResult { super(results, sequenceId); mPartialResults = new ArrayList<>(); + mSessionId = CameraCaptureSession.SESSION_ID_NONE; } /** @@ -95,4 +99,14 @@ public final class TotalCaptureResult extends CaptureResult { 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 c74204d63c96..3c195298b759 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -156,9 +156,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } 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 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { 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 1e680dfd21b3..ff4ad79f120f 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -585,8 +585,8 @@ public class CameraDeviceImpl extends CameraDevice { 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 @@ public class CameraDeviceImpl extends CameraDevice { 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 @@ public class CameraDeviceImpl extends CameraDevice { 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 @@ public class CameraDeviceImpl extends CameraDevice { 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 @@ public class CameraDeviceImpl extends CameraDevice { mHandler = handler; mRequestList = new ArrayList<CaptureRequest>(requestList); mCallback = callback; + mSessionId = sessionId; } public boolean isRepeating() { @@ -1140,6 +1143,10 @@ public class CameraDeviceImpl extends CameraDevice { return mHandler; } + public int getSessionId() { + return mSessionId; + } + } /** @@ -1643,8 +1650,8 @@ public class CameraDeviceImpl extends CameraDevice { 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 @@ public class CameraDeviceImpl extends CameraDevice { 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 691798f71991..a4d6be091bc5 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -326,6 +326,9 @@ public class RequestThreadManager { } 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 481fc2fb6f86..1b57055e0779 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1440,7 +1440,8 @@ public class InputMethodService extends AbstractInputMethodService { 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 @@ public class InputMethodService extends AbstractInputMethodService { 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 63f48cff84c3..c531e7ec8f69 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -98,13 +98,41 @@ public class ConnectivityManager { 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 @@ public class ConnectivityManager { /** * 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 class ConnectivityManager { 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 @@ public class ConnectivityManager { * * <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 @@ public class ConnectivityManager { * * <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 @@ public class ConnectivityManager { * {@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 @@ public class ConnectivityManager { * <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 @@ public class ConnectivityManager { } } + /** {@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 @@ public class ConnectivityManager { * @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 efc76b34a4ab..77200a57e498 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -98,6 +98,8 @@ interface IConnectivityManager 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 43309c0a43e5..8c1f44fa1a69 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -115,15 +115,15 @@ import java.util.Set; * 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 @@ public final class Parcel { } /** + * 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 @@ public final class Parcel { } /** + * 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 0b1031cfbe71..16e0bf7476c9 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 @@ public interface IMountService extends IInterface { } @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 @@ public interface IMountService extends IInterface { 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 interface IMountService extends IInterface { 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 747fb40b92c7..6116aef86ba5 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -22,6 +22,8 @@ import android.annotation.NonNull; 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 @@ public class StorageManager { } } - /** {@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 @@ public class StorageManager { } } - /** {@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 69a05c4f0b19..bae06b80f5ca 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.provider.BrowserContract.Searches; 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 @@ public class Browser { * {@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 @@ public class Browser { * {@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 @@ public class Browser { * * @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 @@ public class Browser { * * @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 @@ public class Browser { * @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 @@ public class Browser { * @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 @@ public class Browser { * * @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 @@ public class Browser { * @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 @@ public class Browser { * 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 @@ public class Browser { * @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 @@ public class Browser { * @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 @@ public class Browser { * @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 @@ public class Browser { * @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 @@ public class Browser { * 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 @@ public class Browser { * @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 cc7f88053a00..35b8819f3048 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -358,6 +358,20 @@ public abstract class NotificationListenerService extends Service { } /** + * 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 04179219b73a..4d1209a9cb8b 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 @@ public class AnimationUtils { 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 @@ public class AnimationUtils { } 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 c6b4d7e3aaf3..113e597bedfd 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -196,9 +196,23 @@ class DayPickerView extends ViewGroup { } @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 9ecdc9c35214..c95977445b47 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1854,20 +1854,19 @@ public class GridView extends AbsListView { 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 a50941b76ee1..10e4db36ef1f 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -1335,10 +1335,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback // 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 0249c222d969..2778f0f9df7d 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -162,7 +162,6 @@ class SimpleMonthView extends View { mTitleFormatter = new SimpleDateFormat(titleFormat, locale); mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale); - setClickable(true); initPaints(res); } @@ -318,7 +317,8 @@ class SimpleMonthView extends View { 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 @@ class SimpleMonthView extends View { 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 @@ class SimpleMonthView extends View { 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 @@ class SimpleMonthView extends View { 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 @@ class SimpleMonthView extends View { 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 @@ class SimpleMonthView extends View { } 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 @@ class SimpleMonthView extends View { } @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 @@ class SimpleMonthView extends View { 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 @@ class SimpleMonthView extends View { 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 f7e96489c569..c0c8aecf9652 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -403,7 +403,7 @@ public class ZoomButtonsController implements View.OnTouchListener { // 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 @@ public class ZoomButtonsController implements View.OnTouchListener { 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 89990c2edd00..191662c1e621 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -111,7 +111,7 @@ public class SwipeDismissLayout extends FrameLayout { } 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 @@ public class SwipeDismissLayout extends FrameLayout { 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 @@ public class SwipeDismissLayout extends FrameLayout { 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 @@ public class SwipeDismissLayout extends FrameLayout { // 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 5c08dafe5468..441e640c5884 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.graphics.drawable.Drawable; 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.ViewParent; 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 @@ import java.util.Comparator; */ 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { // "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 @@ public class ViewPager extends ViewGroup { mAdapterChangeListener = listener; } - private int getClientWidth() { + private int getPaddedWidth() { return getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); } @@ -504,36 +510,33 @@ public class ViewPager extends ViewGroup { 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; + return false; } + + item = MathUtils.constrain(item, 0, mAdapter.getCount() - 1); 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; - } 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { ii.offset = offset; offset += ii.widthFactor + marginOffset; } - - mNeedCalculatePageOffsets = false; } /** @@ -1546,34 +1569,47 @@ public class ViewPager extends ViewGroup { // 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 @@ public class ViewPager extends ViewGroup { @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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { } 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { // 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 @@ public class ViewPager extends ViewGroup { @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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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(); + final EdgeEffect startEdge; + final EdgeEffect endEdge; + if (isLayoutRtl()) { + startEdge = mRightEdge; + endEdge = mLeftEdge; + } else { + startEdge = mLeftEdge; + endEdge = mRightEdge; + } - float leftBound = width * mFirstOffset; - float rightBound = width * mLastOffset; - boolean leftAbsolute = true; - boolean rightAbsolute = true; + // Translate scroll to relative coordinates. + final float nextScrollX = getScrollX() + deltaX; + final float scrollStart; + if (isLayoutRtl()) { + scrollStart = MAX_SCROLL_X - nextScrollX; + } else { + scrollStart = nextScrollX; + } - final ItemInfo firstItem = mItems.get(0); - final ItemInfo lastItem = mItems.get(mItems.size() - 1); - if (firstItem.position != 0) { - leftAbsolute = false; - leftBound = firstItem.offset * width; + 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; } - if (lastItem.position != mAdapter.getCount() - 1) { - rightAbsolute = false; - rightBound = lastItem.offset * width; + + 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; } - if (scrollX < leftBound) { - if (leftAbsolute) { - float over = leftBound - scrollX; - mLeftEdge.onPull(Math.abs(over) / width); + 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 @@ public class ViewPager extends ViewGroup { * @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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 { + itemOffset = offset; + widthFactor = mAdapter.getPageWidth(pos); + } + + final float left; + final float scaledOffset = itemOffset * width; + if (isLayoutRtl()) { + left = MAX_SCROLL_X - scaledOffset; } else { - float widthFactor = mAdapter.getPageWidth(pos); - drawAt = (offset + widthFactor) * width; - offset += widthFactor + marginOffset; + left = scaledOffset + widthFactor * width; } - if (drawAt + mPageMargin > scrollX) { - mMarginDrawable.setBounds((int) drawAt, mTopPageBounds, - (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds); + 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { 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 @@ public class ViewPager extends ViewGroup { } 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 8b2c26949c5d..2f6a69c269ca 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -189,32 +189,44 @@ sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audio 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 @@ static JNINativeMethod gMethods[] = { {"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 62685a1898f7..018c1a1b6ed5 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 index 4e6d8e304aee..8570d26e9030 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 index dc2470694fec..d412ce0f3766 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 index 5a930c4d5ad3..5a930c4d5ad3 100644 --- a/core/res/res/drawable/list_highlight_material.xml +++ b/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 8125544cc2c1..21503415b857 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 1852bfa58731..b582d748d15b 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 00c771d8b14a..887b0a56e1b1 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 baccafd191ce..875659d8c085 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 6f554f08ce17..51c2062d806e 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 db178fa72258..f81ee8c12597 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -614,7 +614,7 @@ please see styles_device_defaults.xml. <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 7e2415088600..5b133252de03 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 a413d917cf62..e8aab07f56f4 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -129,8 +129,8 @@ please see themes_device_defaults.xml. <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 @@ please see themes_device_defaults.xml. <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 d674f7ff9356..5ccdcb9453ec 100644 --- a/docs/html/google/play-services/index.jd +++ b/docs/html/google/play-services/index.jd @@ -74,30 +74,8 @@ announcement <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 19f510afb1d8..2a908ba5fe48 100644 --- a/docs/html/guide/appendix/media-formats.jd +++ b/docs/html/guide/appendix/media-formats.jd @@ -71,7 +71,7 @@ page.tags=video,audio,mpeg,mp4,m4a,mp3,3gp,3gpp,flac,wave,wav </tr> <tr> -<td rowspan="11">Audio</td> +<td rowspan="12">Audio</td> <td>AAC LC</td> <td style="text-align: center;"><big>•</big></td> <td style="text-align: center;"><big>•</big></td> @@ -180,6 +180,15 @@ rates for raw PCM recordings at 8000, 16000 and 44100 Hz.</td> </tr> <tr> +<td>Opus</td> +<td style="text-align: center;"></td> +<td style="text-align: center;"><big>•</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>•</big></td> @@ -235,7 +244,7 @@ rates for raw PCM recordings at 8000, 16000 and 44100 Hz.</td> <tr> -<td rowspan="4">Video</td> +<td rowspan="6">Video</td> <td>H.263</td> <td style="text-align: center;"><big>•</big></td> <td style="text-align: center;"><big>•</big></td> @@ -257,6 +266,15 @@ rates for raw PCM recordings at 8000, 16000 and 44100 Hz.</td> </tr> <tr> +<td>H.265 HEVC</td> +<td style="text-align: center;" nowrap></td> +<td style="text-align: center;" nowrap><big>•</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> + • MPEG-4 (.mp4)<br> +</tr> + +<tr> <td>MPEG-4 SP</td> <td> </td> <td style="text-align: center;"><big>•</big></td> @@ -275,6 +293,16 @@ rates for raw PCM recordings at 8000, 16000 and 44100 Hz.</td> • 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>•</big><br><small>(Android 4.4+)</small></td> +<td></td> +<td> + • <a href="http://www.webmproject.org/">WebM</a> (.webm)<br> + • 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 2766e90a39a5..0f5871fc2ebc 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 @@ startpage=true <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 @@ startpage=true 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 @@ startpage=true 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 5fae831de8d7..3b25ba66b9c3 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 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { } 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 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { 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 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { 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 @@ public abstract class AndroidKeyPairGenerator extends KeyPairGeneratorSpi { 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 1c9d005e00f9..000000000000 --- 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 a02dc33c489c..f58e48ae2c0f 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 964cd7ed24df..4ea27efc7909 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 5af0527ac51c..8c49ff0a3eca 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.KeymasterDefs; import android.security.keymaster.OperationResult; import android.util.Log; +import java.security.InvalidKeyException; import java.util.Locale; /** @@ -87,9 +88,9 @@ public class KeyStore { 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 class KeyStore { } } - 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 class KeyStore { } } - 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 @@ public class KeyStore { // 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 37e00b28cd83..3b13e83520bb 100644 --- a/keystore/java/android/security/KeyStoreCipherSpi.java +++ b/keystore/java/android/security/KeyStoreCipherSpi.java @@ -136,6 +136,14 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry 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 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry @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 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry mOperationToken = null; mOperationHandle = null; mMainDataStreamer = null; + mCachedException = null; } private void resetWhilePreservingInitState() { @@ -218,12 +260,17 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry 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 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry 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 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry @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 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry 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 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry @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 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: throw new AEADBadTagException(); default: - throw KeyStore.getCryptoOperationException(e); + throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); } } @@ -584,11 +653,11 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry 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 8ed6e04ddfe8..1aa3aecc0b1e 100644 --- a/keystore/java/android/security/KeyStoreConnectException.java +++ b/keystore/java/android/security/KeyStoreConnectException.java @@ -21,7 +21,7 @@ package android.security; * * @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 aafd2fa97aff..06191994af62 100644 --- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java @@ -136,7 +136,7 @@ public class KeyStoreCryptoOperationChunkedStreamer { // 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 @@ public class KeyStoreCryptoOperationChunkedStreamer { 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 @@ public class KeyStoreCryptoOperationChunkedStreamer { 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 @@ public class KeyStoreCryptoOperationChunkedStreamer { 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 @@ public class KeyStoreCryptoOperationChunkedStreamer { } 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 a19bbda3b9ec..175369ce91d0 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -69,9 +69,10 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp 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,49 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp @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"); + } + + private void resetAll() { + mKey = null; + IBinder operationToken = mOperationToken; + if (operationToken != null) { + mOperationToken = null; + mKeyStore.abort(operationToken); } - engineReset(); - ensureKeystoreOperationInitialized(); + mOperationHandle = null; + mChunkedStreamer = null; } - @Override - protected void engineReset() { + private void resetWhilePreservingInitState() { IBinder operationToken = mOperationToken; if (operationToken != null) { mOperationToken = null; @@ -120,11 +142,16 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp mChunkedStreamer = null; } - private void ensureKeystoreOperationInitialized() { + @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 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp 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 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp 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 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp @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 d1abe12d6353..293c4c9427f2 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -210,7 +210,8 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { 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 806b214d5f44..4fe210bde918 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 f5f5f41d941e..66f4dd82c7d4 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 7a88deefa791..a7046dd29cec 100644 --- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java +++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java @@ -18,7 +18,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 android.test.AndroidTestCase; @@ -768,7 +768,7 @@ public class AndroidKeyStoreTest extends AndroidTestCase { 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 @@ public class AndroidKeyStoreTest extends AndroidTestCase { 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 @@ public class AndroidKeyStoreTest extends AndroidTestCase { { 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 @@ public class AndroidKeyStoreTest extends AndroidTestCase { { 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 @@ public class AndroidKeyStoreTest extends AndroidTestCase { { 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 @@ public class AndroidKeyStoreTest extends AndroidTestCase { { 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 @@ public class AndroidKeyStoreTest extends AndroidTestCase { 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 @@ public class AndroidKeyStoreTest extends AndroidTestCase { 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 1a5552a18df8..916b1ba9ac46 100644 --- a/keystore/tests/src/android/security/KeyStoreTest.java +++ b/keystore/tests/src/android/security/KeyStoreTest.java @@ -32,7 +32,7 @@ import android.test.ActivityUnitTestCase; 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 class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { 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 5fca8ec9a358..836f86875dcd 100644 --- a/libs/hwui/Android.common.mk +++ b/libs/hwui/Android.common.mk @@ -24,6 +24,7 @@ LOCAL_SRC_FILES := \ 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 c92ab91d21bf..f535afb2d61a 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -127,7 +127,7 @@ private: } 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 51898d2a1d22..b6f0baf4bf3e 100644 --- a/libs/hwui/tests/Android.mk +++ b/libs/hwui/tests/Android.mk @@ -34,16 +34,3 @@ LOCAL_SRC_FILES += \ 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 000000000000..51601b072405 --- /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 index 166d5b6e92f6..166d5b6e92f6 100644 --- a/libs/hwui/tests/ClipAreaTests.cpp +++ b/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 000000000000..b3959d169e1d --- /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 000000000000..a2d6a34726df --- /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 000000000000..c9b96360b36b --- /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 000000000000..59b12cf66a89 --- /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 000000000000..d90dd825ea1d --- /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 ff6fed23dc2c..a7e092fc04d4 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -55,16 +55,16 @@ public class AudioFormat { 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 class AudioFormat { 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 cb70e8b722a9..d851ad7b96bc 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -3147,6 +3147,20 @@ public class AudioManager { "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 2346abec81e9..201a7962db0e 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -59,7 +59,7 @@ public class AudioRecord /** 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 847bdc290219..9bd5c5504f0e 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -96,7 +96,7 @@ public class AudioTrack /** 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 @@ public class AudioTrack 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 @@ public class AudioTrack 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 @@ public class AudioTrack } 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 @@ public class AudioTrack * 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 @@ public class AudioTrack * {@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 @@ public class AudioTrack * 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 @@ public class AudioTrack // 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 75901fdb615a..195c98704e4c 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -50,10 +50,25 @@ public abstract class Image implements AutoCloseable { /** * @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 @@ public abstract class Image implements AutoCloseable { * 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 @@ public abstract class Image implements AutoCloseable { * @param timestamp The timestamp to be set for this image. */ public void setTimestamp(long timestamp) { + throwISEIfImageIsInvalid(); return; } @@ -187,6 +203,7 @@ public abstract class Image implements AutoCloseable { * </p> */ public boolean isOpaque() { + throwISEIfImageIsInvalid(); return false; } @@ -199,6 +216,8 @@ public abstract class Image implements AutoCloseable { * 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 @@ public abstract class Image implements AutoCloseable { * 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 @@ public abstract class Image implements AutoCloseable { * a new owner. */ boolean isAttachable() { + throwISEIfImageIsInvalid(); + return false; } @@ -279,6 +302,8 @@ public abstract class Image implements AutoCloseable { * @return The owner of the Image. */ Object getOwner() { + throwISEIfImageIsInvalid(); + return null; } @@ -294,6 +319,8 @@ public abstract class Image implements AutoCloseable { * @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 e54525dce948..6d3020808758 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -368,7 +368,7 @@ public class ImageReader implements AutoCloseable { 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 @@ public class ImageReader implements AutoCloseable { si.clearSurfacePlanes(); nativeReleaseImage(i); - si.setImageValid(false); + si.mIsImageValid = false; } /** @@ -686,7 +686,6 @@ public class ImageReader implements AutoCloseable { private class SurfaceImage extends android.media.Image { public SurfaceImage(int format) { - mIsImageValid = false; mFormat = format; } @@ -784,16 +783,6 @@ public class ImageReader implements AutoCloseable { 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 @@ public class ImageReader implements AutoCloseable { 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 c18b46386a80..f80533914b57 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -29,7 +29,6 @@ import java.nio.ByteOrder; import java.nio.NioUtils; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; /** * <p> @@ -204,7 +203,7 @@ public class ImageWriter implements AutoCloseable { WriterSurfaceImage newImage = new WriterSurfaceImage(this); nativeDequeueInputImage(mNativeContext, newImage); mDequeuedImages.add(newImage); - newImage.setImageValid(true); + newImage.mIsImageValid = true; return newImage; } @@ -260,7 +259,7 @@ public class ImageWriter implements AutoCloseable { 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 @@ public class ImageWriter implements AutoCloseable { // 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 @@ public class ImageWriter implements AutoCloseable { WriterSurfaceImage wi = (WriterSurfaceImage) image; - if (!wi.isImageValid()) { + if (!wi.mIsImageValid) { throw new IllegalStateException("Image is invalid"); } @@ -568,7 +567,7 @@ public class ImageWriter implements AutoCloseable { cancelImage(mNativeContext, image); mDequeuedImages.remove(image); wi.clearSurfacePlanes(); - wi.setImageValid(false); + wi.mIsImageValid = false; } private boolean isImageOwnedByMe(Image image) { @@ -585,7 +584,6 @@ public class ImageWriter implements AutoCloseable { 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 @@ public class ImageWriter implements AutoCloseable { @Override public int getFormat() { - if (!mIsImageValid.get()) { - throw new IllegalStateException("Image is already released"); - } + throwISEIfImageIsInvalid(); + if (mFormat == -1) { mFormat = nativeGetFormat(); } @@ -615,9 +612,7 @@ public class ImageWriter implements AutoCloseable { @Override public int getWidth() { - if (!mIsImageValid.get()) { - throw new IllegalStateException("Image is already released"); - } + throwISEIfImageIsInvalid(); if (mWidth == -1) { mWidth = nativeGetWidth(); @@ -628,9 +623,7 @@ public class ImageWriter implements AutoCloseable { @Override public int getHeight() { - if (!mIsImageValid.get()) { - throw new IllegalStateException("Image is already released"); - } + throwISEIfImageIsInvalid(); if (mHeight == -1) { mHeight = nativeGetHeight(); @@ -641,36 +634,28 @@ public class ImageWriter implements AutoCloseable { @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 @@ public class ImageWriter implements AutoCloseable { @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 @@ public class ImageWriter implements AutoCloseable { @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 @@ public class ImageWriter implements AutoCloseable { } } - 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 @@ public class ImageWriter implements AutoCloseable { @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 1f00c7b03090..d22cfdac235e 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -2059,7 +2059,6 @@ final public class MediaCodec { /** @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 @@ final public class MediaCodec { 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 @@ final public class MediaCodec { * 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 @@ final public class MediaCodec { 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 @@ final public class MediaCodec { 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 @@ final public class MediaCodec { @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 3b4f8e5e5f7b..dc6760d59f71 100644 --- a/media/java/android/media/MediaSync.java +++ b/media/java/android/media/MediaSync.java @@ -49,7 +49,7 @@ import java.util.List; * 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 @@ import java.util.List; * } * * // 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 @@ import java.util.List; * <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 @@ final public class MediaSync { * @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 @@ final public class MediaSync { 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 @@ final public class MediaSync { } /** + * 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 @@ final public class MediaSync { 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 754facd1a4a2..966e41a1bb01 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 @@ public final class TvContentRating { 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 @@ public final class TvContentRating { * @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 f0e2f5c5e0cf..46d33b468982 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 @@ public final class TvInputInfo implements Parcelable { * @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 @@ public final class TvInputInfo implements Parcelable { * @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 @@ public final class TvInputInfo implements Parcelable { * @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 105084e3fd3e..601fa459e7cb 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.KeyEvent; 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 @@ public final class TvInputManager { * @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 @@ public final class TvInputManager { * @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 @@ public final class TvInputManager { * * @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 @@ public final class TvInputManager { * 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 @@ public final class TvInputManager { * @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 @@ public final class TvInputManager { * @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 @@ public final class TvInputManager { * @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 @@ public final class TvInputManager { * @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 @@ public final class TvInputManager { * 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 @@ public final class TvInputManager { * * @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 @@ public final class TvInputManager { * * @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 @@ public final class TvInputManager { * 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 @@ public final class TvInputManager { /** * 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 @@ public final class TvInputManager { /** * 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 382ec91b5e80..5156ae82f29d 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.view.accessibility.CaptioningManager; 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 @@ public abstract class TvInputService extends Service { * @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 @@ public abstract class TvInputService extends Service { * @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 @@ public abstract class TvInputService extends Service { * @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 6eedeb43c36e..2c956e9d3e8e 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 @@ public final class TvTrackInfo implements Parcelable { * @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 645604996d6f..d248b1232221 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 @@ public class TvView extends ViewGroup { * @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 @@ public class TvView extends ViewGroup { * @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 d2c614e03b35..8c7666521df7 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -338,7 +338,7 @@ static void ImageWriter_dequeueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, 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 6f336725eab5..d71b44bf549a 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 @@ package com.android.mediaframeworktest.integration; 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 @@ public class CameraDeviceBinderTest extends AndroidTestCase { 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 2ec15bed5ca4..aea8585dce52 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 e4054ac20656..b86fc4b3315d 100644 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java @@ -56,24 +56,13 @@ public class CaptivePortalLoginActivity extends Activity { 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 @@ public class CaptivePortalLoginActivity extends Activity { 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 @@ public class CaptivePortalLoginActivity extends Activity { 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 @@ public class CaptivePortalLoginActivity extends Activity { 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 @@ public class CaptivePortalLoginActivity extends Activity { } } - private void done(int result) { + private void done(Result result) { if (mNetworkCallback != null) { - ConnectivityManager.from(this).unregisterNetworkCallback(mNetworkCallback); + mCm.unregisterNetworkCallback(mNetworkCallback); + } + 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; } - 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); finish(); } @@ -190,11 +181,11 @@ public class CaptivePortalLoginActivity extends Activity { 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 @@ public class CaptivePortalLoginActivity extends Activity { 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 e0af29d3b92d..1cf7248b1151 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.HandlerThread; 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 @@ public class StorageMeasurement { 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 class StorageMeasurement { } public interface MeasurementReceiver { - public void onDetailsChanged(MeasurementDetails details); + void onDetailsChanged(MeasurementDetails details); } private WeakReference<MeasurementReceiver> mReceiver; @@ -370,6 +374,10 @@ public class StorageMeasurement { } } + 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 35e9636f3649..5b4b4fd00b76 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 292c9c226384..0d331d1a93cf 100755 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -328,7 +328,7 @@ public class BatteryMeterView extends View implements DemoMode, 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 b828e78a8c57..7b555fc5257c 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 @@ public class KeyguardViewMediator extends SystemUI { 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 de4874f31285..e542264a5162 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 @@ public abstract class BaseStatusBar extends SystemUI implements 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 @@ public abstract class BaseStatusBar extends SystemUI implements 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 c854d63b930f..d05889247801 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 95d0b4276c9b..c94623a72253 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -402,6 +402,7 @@ android.app.SharedPreferencesImpl$EditorImpl 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 3e5eee86a777..ef82bb70cec4 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.io.PrintWriter; 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 7d8e9de798cc..12a99b01f6d8 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2608,6 +2608,16 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + 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 89a717350798..7172ab78b8b8 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.ComponentName; 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 @@ class MountService extends IMountService.Stub /** 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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub @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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub } 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 @@ class MountService extends IMountService.Stub } } + 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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub } @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 { + public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { + enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + waitForReady(); + synchronized (mLock) { - Slog.d(TAG, "Changing primary storage UUID to " + volumeUuid); - mPrimaryStorageUuid = volumeUuid; - writeMetadataLocked(); + 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); + } - // TODO: reevaluate all volumes we know about! + 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 @@ class MountService extends IMountService.Stub @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 @@ class MountService extends IMountService.Stub warnOnNotMounted(); final ObbState state; - synchronized (mObbPathToStateMap) { + synchronized (mObbMounts) { state = mObbPathToStateMap.get(rawPath); } if (state == null) { @@ -1843,7 +1942,7 @@ class MountService extends IMountService.Stub Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); final ObbState existingState; - synchronized (mObbPathToStateMap) { + synchronized (mObbMounts) { existingState = mObbPathToStateMap.get(rawPath); } @@ -2093,6 +2192,8 @@ class MountService extends IMountService.Stub @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 @@ class MountService extends IMountService.Stub 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 1b32f577b12b..999e91bb9714 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 @@ public class AccountManagerService 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 @@ public class AccountManagerService 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 @@ public class AccountManagerService 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 @@ public class AccountManagerService 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 class AccountManagerService 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 @@ public class AccountManagerService mCreationTime = SystemClock.elapsedRealtime(); mAccountName = accountName; mAuthDetailsRequired = authDetailsRequired; + mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime; synchronized (mSessions) { mSessions.put(toString(), this); @@ -2651,15 +2667,55 @@ public class AccountManagerService 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 @@ public class AccountManagerService 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 a48a4d903b6a..4970e0fe7d84 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 9a30f0dd8371..6b56279354ba 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; 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.ArrayList; 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 @@ public class AudioService extends IAudioService.Stub { 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 class AudioService extends IAudioService.Stub { } } 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 @@ public class AudioService extends IAudioService.Stub { 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 @@ public class AudioService extends IAudioService.Stub { // 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 @@ public class AudioService extends IAudioService.Stub { 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 7e202768cffc..4e83992bc519 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 @@ public class NetworkMonitor extends StateMachine { 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 @@ public class NetworkMonitor extends StateMachine { /** * 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 class NetworkMonitor extends StateMachine { public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 10; /** - * Message to self indicating sign-in app bypassed captive portal. - */ - private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 11; - - /** - * Message to self indicating no sign-in app responded. + * 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_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 @@ public class NetworkMonitor extends StateMachine { 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 @@ public class NetworkMonitor extends StateMachine { 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 @@ public class NetworkMonitor extends StateMachine { 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 @@ public class NetworkMonitor extends StateMachine { // 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 @@ public class NetworkMonitor extends StateMachine { 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 @@ public class NetworkMonitor extends StateMachine { } } - 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 28597c1548da..6b5908d78126 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 android.hardware.fingerprint.IFingerprintServiceReceiver; 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 @@ public class FingerprintService extends SystemService { 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 class FingerprintService extends SystemService { public void binderDied() { token = null; removeClient(this); + receiver = null; } protected void finalize() throws Throwable { @@ -406,10 +406,9 @@ public class FingerprintService extends SystemService { * @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 @@ public class FingerprintService extends SystemService { * @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 @@ public class FingerprintService extends SystemService { * @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 @@ public class FingerprintService extends SystemService { * @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 @@ public class FingerprintService extends SystemService { * @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 1008653321e3..25998da26785 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; 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 android.widget.Toast; 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 @@ public class NotificationManagerService extends SystemService { ArrayList<String> mLights = new ArrayList<>(); private AppOpsManager mAppOps; + private UsageStatsManagerInternal mAppUsageStats; private Archive mArchive; @@ -871,6 +876,7 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { } } + @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 5569a09d13df..e106a4af8bc9 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 @@ public final class NotificationRecord { 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 @@ public final class NotificationRecord { 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 db3ae9192e96..c8e5c3a08d08 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 class KeySetManagerService { } 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 bd225247bfb2..f087c33645a8 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 class PackageManagerService extends IPackageManager.Stub { 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 25857c5d5062..5536d4dd5f19 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } 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 e914cd4e7e36..a210223dc079 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 @@ class AppWindowToken extends WindowToken { 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 bb535342d0ad..c24fcb3e7501 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 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { 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 897b865d92b1..52071cca29b9 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 @@ public class WindowAnimator { 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 38fc95957291..c5bdbb03e229 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 cd9689e46aa1..ec708797f0e6 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 @@ final class WindowState implements WindowManagerPolicy.WindowState { 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 bbeee8ca7566..b303505ca92a 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 @@ class WindowToken { 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 a49dbded5f54..2b88158c62b2 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + " 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * 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 25b80936f944..c0e1d196e7f5 100644 --- a/services/net/java/android/net/dhcp/DhcpAckPacket.java +++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java @@ -29,9 +29,9 @@ class DhcpAckPacket extends DhcpPacket { */ 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 ab564932ef3e..e1d17879748b 100644 --- a/services/net/java/android/net/dhcp/DhcpClient.java +++ b/services/net/java/android/net/dhcp/DhcpClient.java @@ -154,6 +154,7 @@ public class DhcpClient extends BaseDhcpStateMachine { 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 @@ public class DhcpClient extends BaseDhcpStateMachine { } } - private void initTransactionId() { + private void startNewTransaction() { mTransactionId = mRandom.nextInt(); + mTransactionStartMillis = SystemClock.elapsedRealtime(); } private boolean initSockets() { @@ -344,6 +346,10 @@ public class DhcpClient extends BaseDhcpStateMachine { } } + 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 @@ public class DhcpClient extends BaseDhcpStateMachine { 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 @@ public class DhcpClient extends BaseDhcpStateMachine { 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 @@ public class DhcpClient extends BaseDhcpStateMachine { @Override public void enter() { super.enter(); - initTransactionId(); + startNewTransaction(); } protected boolean sendPacket() { @@ -776,7 +783,7 @@ public class DhcpClient extends BaseDhcpStateMachine { @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 4a22b65d0101..7ecdea7b89da 100644 --- a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java +++ b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java @@ -26,10 +26,10 @@ class DhcpDeclinePacket extends DhcpPacket { /** * 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 ed0fdc667419..91e6bd6469d8 100644 --- a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java +++ b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java @@ -26,8 +26,8 @@ class DhcpDiscoverPacket extends DhcpPacket { /** * 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 2434fc92a83f..7a83466c6e05 100644 --- a/services/net/java/android/net/dhcp/DhcpInformPacket.java +++ b/services/net/java/android/net/dhcp/DhcpInformPacket.java @@ -26,10 +26,10 @@ class DhcpInformPacket extends DhcpPacket { /** * 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 1390ea7d1031..6458232b8fa0 100644 --- a/services/net/java/android/net/dhcp/DhcpNakPacket.java +++ b/services/net/java/android/net/dhcp/DhcpNakPacket.java @@ -26,10 +26,10 @@ class DhcpNakPacket extends DhcpPacket { /** * 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 b1f3bbd4a3df..af41708a97c8 100644 --- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java +++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java @@ -31,9 +31,9 @@ class DhcpOfferPacket extends DhcpPacket { /** * 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 a64ee6f10095..b923b1bf25fb 100644 --- a/services/net/java/android/net/dhcp/DhcpPacket.java +++ b/services/net/java/android/net/dhcp/DhcpPacket.java @@ -226,6 +226,11 @@ abstract class DhcpPacket { 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 class DhcpPacket { */ 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 @@ abstract class DhcpPacket { 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 @@ abstract class DhcpPacket { { // bootp parameters int transactionId; + short secs; Inet4Address clientIp; Inet4Address yourIp; Inet4Address nextIp; @@ -759,7 +766,7 @@ abstract class DhcpPacket { 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 @@ abstract class DhcpPacket { 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 @@ abstract class DhcpPacket { * 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 @@ abstract class DhcpPacket { 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 @@ abstract class DhcpPacket { 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 @@ abstract class DhcpPacket { */ 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 @@ abstract class DhcpPacket { * 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 5d378b83d795..4f9aa01151ca 100644 --- a/services/net/java/android/net/dhcp/DhcpRequestPacket.java +++ b/services/net/java/android/net/dhcp/DhcpRequestPacket.java @@ -28,9 +28,9 @@ class DhcpRequestPacket extends DhcpPacket { /** * 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 2658937fdb1e..4f7c7ec8f574 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 @@ public class DhcpPacketTest extends TestCase { 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 3d54dfbcffb8..04984d3e5dff 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.UsageEvents.Event; 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } /** + * 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 @@ public class UsageStatsService extends SystemService implements } 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements } @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 0a9481acf70c..d94759d0a685 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 @@ class UserUsageStatsService { 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 8f0c6c8f27c5..daccf959cadb 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 @@ public final class UsbAlsaManager { // 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 2a3038475b23..d92c0c7d0ba1 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -933,7 +933,8 @@ public final class Call { 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 bab60fe58543..0424548fd1ad 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -50,6 +50,7 @@ public abstract class Conference implements Conferenceable { 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 @@ public abstract class Conference implements Conferenceable { 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 @@ public abstract class Conference implements Conferenceable { 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 e79584f85200..cd10050935b1 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -28,6 +28,7 @@ import android.view.Surface; 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 abstract class Connection implements Conferenceable { */ 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 @@ public abstract class Connection implements Conferenceable { 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 @@ public abstract class Connection implements Conferenceable { @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 @@ public abstract class Connection implements Conferenceable { * IVideoProvider stub implementation. */ private final class VideoProviderBinder extends IVideoProvider.Stub { - public void setVideoCallback(IBinder videoCallbackBinder) { + public void addVideoCallback(IBinder videoCallbackBinder) { + mMessageHandler.obtainMessage( + MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget(); + } + + public void removeVideoCallback(IBinder videoCallbackBinder) { mMessageHandler.obtainMessage( - MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget(); + MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget(); } public void setCamera(String cameraId) { @@ -609,21 +640,23 @@ public abstract class Connection implements Conferenceable { 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 abstract class Connection implements Conferenceable { */ 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 @@ public abstract class Connection implements Conferenceable { * @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 9812815c6e8a..c039acfb52fa 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -427,6 +427,12 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { 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 eef72fb198c4..bf8fac621081 100644 --- a/telecomm/java/android/telecom/DefaultDialerManager.java +++ b/telecomm/java/android/telecom/DefaultDialerManager.java @@ -87,15 +87,15 @@ public class DefaultDialerManager { } // 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 73bcd0c422cb..3a7faf645a8c 100644 --- a/telecomm/java/android/telecom/DisconnectCause.java +++ b/telecomm/java/android/telecom/DisconnectCause.java @@ -130,8 +130,10 @@ public final class DisconnectCause implements Parcelable { /** * 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 @@ public final class DisconnectCause implements Parcelable { /** * 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 7cbc0fc4beb1..caa173183ea2 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -370,6 +370,11 @@ public abstract class InCallService extends Service { 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 c5c3d11a1690..1a3091011b01 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -46,6 +46,7 @@ public final class ParcelableCall implements Parcelable { 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 @@ public final class ParcelableCall implements Parcelable { int callerDisplayNamePresentation, GatewayInfo gatewayInfo, PhoneAccountHandle accountHandle, + boolean isVideoCallProviderChanged, IVideoProvider videoCallProvider, String parentCallId, List<String> childCallIds, @@ -89,6 +91,7 @@ public final class ParcelableCall implements Parcelable { mCallerDisplayNamePresentation = callerDisplayNamePresentation; mGatewayInfo = gatewayInfo; mAccountHandle = accountHandle; + mIsVideoCallProviderChanged = isVideoCallProviderChanged; mVideoCallProvider = videoCallProvider; mParentCallId = parentCallId; mChildCallIds = childCallIds; @@ -232,6 +235,18 @@ public final class ParcelableCall implements Parcelable { 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 @@ public final class ParcelableCall implements Parcelable { 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 @@ public final class ParcelableCall implements Parcelable { callerDisplayNamePresentation, gatewayInfo, accountHandle, + isVideoCallProviderChanged, videoCallProvider, parentCallId, childCallIds, @@ -313,6 +330,7 @@ public final class ParcelableCall implements Parcelable { 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 ab825491a57c..e54e79de6056 100644 --- a/telecomm/java/android/telecom/ParcelableConference.java +++ b/telecomm/java/android/telecom/ParcelableConference.java @@ -34,9 +34,10 @@ public final class ParcelableConference implements Parcelable { 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; + private StatusHints mStatusHints; public ParcelableConference( PhoneAccountHandle phoneAccount, @@ -44,7 +45,9 @@ public final class ParcelableConference implements Parcelable { int connectionCapabilities, List<String> connectionIds, IVideoProvider videoProvider, - int videoState) { + int videoState, + long connectTimeMillis, + StatusHints statusHints) { mPhoneAccount = phoneAccount; mState = state; mConnectionCapabilities = connectionCapabilities; @@ -52,18 +55,8 @@ public final class ParcelableConference implements Parcelable { mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED; mVideoProvider = videoProvider; mVideoState = videoState; - } - - public ParcelableConference( - PhoneAccountHandle phoneAccount, - int state, - int connectionCapabilities, - List<String> connectionIds, - IVideoProvider videoProvider, - int videoState, - long connectTimeMillis) { - this(phoneAccount, state, connectionCapabilities, connectionIds, videoProvider, videoState); mConnectTimeMillis = connectTimeMillis; + mStatusHints = statusHints; } @Override @@ -113,6 +106,10 @@ public final class ParcelableConference implements Parcelable { return mVideoState; } + public StatusHints getStatusHints() { + return mStatusHints; + } + public static final Parcelable.Creator<ParcelableConference> CREATOR = new Parcelable.Creator<ParcelableConference> () { @Override @@ -124,13 +121,14 @@ public final class ParcelableConference implements Parcelable { 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 @@ public final class ParcelableConference implements Parcelable { 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 c1c1129a2aab..3d9acda688c8 100644 --- a/telecomm/java/android/telecom/Phone.java +++ b/telecomm/java/android/telecom/Phone.java @@ -122,6 +122,11 @@ public final class Phone { 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 @@ public final class Phone { */ 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 4c423f208f12..4ecfd50e705a 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -301,7 +301,7 @@ public final class RemoteConnection { 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 fd9532710224..8d6bda834ede 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -110,6 +110,28 @@ public class TelecomManager { "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 @@ public class TelecomManager { } } + /** + * @deprecated - Use {@link TelecomManager#getDefaultDialerPackage} to directly access + * the default dialer's package name instead. * @hide */ @SystemApi @@ -705,6 +730,40 @@ public class TelecomManager { } /** + * 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 7bef6881f571..3779d1aeca01 100644 --- a/telecomm/java/android/telecom/VideoCallImpl.java +++ b/telecomm/java/android/telecom/VideoCallImpl.java @@ -166,7 +166,7 @@ public class VideoCallImpl extends VideoCall { mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0); mBinder = new VideoCallListenerBinder(); - mVideoProvider.setVideoCallback(mBinder); + mVideoProvider.addVideoCallback(mBinder); } /** {@inheritDoc} */ @@ -175,6 +175,15 @@ public class VideoCallImpl extends VideoCall { } /** {@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 45b24827924d..49f2aadbb000 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -137,6 +137,16 @@ interface ITelecomService { */ 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 e96d9d3d7eb7..bff3865e8840 100644 --- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl +++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl @@ -25,7 +25,9 @@ import android.telecom.VideoProfile; * @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 7d1a2fa9527e..831a19460525 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -208,13 +208,13 @@ public class CarrierConfigManager { } /** - * 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 84d1c54575f8..0443c3edb974 100644 --- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl +++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl @@ -115,4 +115,12 @@ interface IImsCallSessionListener { * - {@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 98d67c0b575c..f1d8d04ea01e 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 b156d0c91958..d2fb0ddd7295 100644 --- a/wifi/java/android/net/wifi/RttManager.java +++ b/wifi/java/android/net/wifi/RttManager.java @@ -808,7 +808,7 @@ public class RttManager { 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 @@ public class RttManager { 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 @@ public class RttManager { } 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 @@ public class RttManager { } @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 @@ public class RttManager { 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 c6f2991f8db2..b7313169b7b4 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -243,6 +243,7 @@ public class WifiConfiguration implements Parcelable { * The band which AP resides on * 0-2G 1-5G * By default, 2G is chosen + * @hide */ public int apBand = 0; @@ -251,6 +252,7 @@ public class WifiConfiguration implements Parcelable { * 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 @@ public class WifiConfiguration implements Parcelable { 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 6917971491d9..3525ec26f53a 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -56,6 +56,8 @@ public class WifiEnterpriseConfig implements Parcelable { /** @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 @@ public class WifiEnterpriseConfig implements Parcelable { } /** + * 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 */ |