diff options
99 files changed, 1801 insertions, 1116 deletions
diff --git a/api/current.txt b/api/current.txt index 2f63509..4ff18d7 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,30 +16435,26 @@ 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(); method public int getAudioFallbackMode(); - method public int getAudioStretchMode(); method public float getPitch(); method public float getSpeed(); method public android.media.PlaybackSettings setAudioFallbackMode(int); - method public android.media.PlaybackSettings setAudioStretchMode(int); method public android.media.PlaybackSettings setPitch(float); method public android.media.PlaybackSettings setSpeed(float); field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0 field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2 field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1 - field public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; // 0x0 - 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 { @@ -17819,6 +17821,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 { @@ -23404,6 +23407,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); @@ -23448,6 +23452,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; } @@ -28444,13 +28449,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(); @@ -28507,7 +28505,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); @@ -28549,7 +28547,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); @@ -28697,12 +28695,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); @@ -28915,6 +28913,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 @@ -30607,6 +30606,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); @@ -30621,6 +30621,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"; @@ -30631,6 +30632,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 5c24e8e..87e130c 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); @@ -13260,6 +13256,7 @@ package android.hardware { method public int getType(); method public java.lang.String getVendor(); method public int getVersion(); + method public boolean isDataInjectionSupported(); method public boolean isWakeUpSensor(); field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0 field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2 @@ -13335,6 +13332,7 @@ package android.hardware { public abstract class SensorManager { method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor); + method public boolean enableDataInjectionMode(boolean); method public boolean flush(android.hardware.SensorEventListener); method public static float getAltitude(float, float); method public static void getAngleChange(float[], float[], float[]); @@ -13347,6 +13345,7 @@ package android.hardware { method public static void getRotationMatrixFromVector(float[], float[]); method public java.util.List<android.hardware.Sensor> getSensorList(int); method public deprecated int getSensors(); + method public boolean injectSensorData(android.hardware.Sensor, float[], int, long); method public deprecated boolean registerListener(android.hardware.SensorListener, int); method public deprecated boolean registerListener(android.hardware.SensorListener, int, int); method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int); @@ -15947,7 +15946,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 +16102,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 +17610,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 +17624,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,30 +17654,26 @@ 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(); method public int getAudioFallbackMode(); - method public int getAudioStretchMode(); method public float getPitch(); method public float getSpeed(); method public android.media.PlaybackSettings setAudioFallbackMode(int); - method public android.media.PlaybackSettings setAudioStretchMode(int); method public android.media.PlaybackSettings setPitch(float); method public android.media.PlaybackSettings setSpeed(float); field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0 field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2 field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1 - field public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; // 0x0 - 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 { @@ -19104,6 +19109,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 { @@ -25293,6 +25299,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); @@ -25337,6 +25344,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; } @@ -30448,13 +30456,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(); @@ -30511,7 +30512,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); @@ -30553,7 +30554,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); @@ -30701,12 +30702,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); @@ -30960,6 +30961,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 @@ -32751,8 +32753,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(); @@ -32771,6 +32774,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"; @@ -32783,6 +32787,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 8ba2a5a..219d35b 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -142,6 +142,8 @@ 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 b84b1e2..39de1dc7 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -30,7 +30,6 @@ import android.content.pm.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 02a329d..da48709 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -16,12 +16,7 @@ package android.animation; -import android.content.res.Configuration; import android.content.res.ConstantState; -import android.content.res.Resources; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.animation.AnimationUtils; import java.util.ArrayList; @@ -30,29 +25,6 @@ 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 e47d017..427ecce 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 f6ad847..6503d89 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 292507b..a455f8b 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -17,12 +17,9 @@ package android.animation; import android.annotation.CallSuper; -import android.content.res.Configuration; -import android.content.res.Resources; import android.os.Looper; import android.os.Trace; import android.util.AndroidRuntimeException; -import android.util.DisplayMetrics; import android.util.Log; import android.view.Choreographer; import android.view.animation.AccelerateDecelerateInterpolator; @@ -564,7 +561,7 @@ 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 ab5f811..9bad9bb 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 10f5960..16a2430 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 e275df0..ac8d5d8 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/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 4ed1489..23659e3 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -30,4 +30,6 @@ 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 bc6099a..8a01d66 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/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 39f4cca..e875864 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -580,6 +580,10 @@ public final class Sensor { private static final int REPORTING_MODE_MASK = 0xE; private static final int REPORTING_MODE_SHIFT = 1; + // MASK for LSB fifth bit. Used to know whether the sensor supports data injection or not. + private static final int DATA_INJECTION_MASK = 0x10; + private static final int DATA_INJECTION_SHIFT = 4; + // TODO(): The following arrays are fragile and error-prone. This needs to be refactored. // Note: This needs to be updated, whenever a new sensor is added. @@ -822,6 +826,20 @@ public final class Sensor { return (mFlags & SENSOR_FLAG_WAKE_UP_SENSOR) != 0; } + /** + * Returns true if the sensor supports data injection when the + * HAL is set to data injection mode. + * + * @return <code>true</code> if the sensor supports data + * injection when the HAL is set in injection mode, + * false otherwise. + * @hide + */ + @SystemApi + public boolean isDataInjectionSupported() { + return (((mFlags & DATA_INJECTION_MASK) >> DATA_INJECTION_SHIFT)) != 0; + } + void setRange(float max, float res) { mMaxRange = max; mResolution = res; diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 34b895b..861969e 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -16,7 +16,10 @@ package android.hardware; +import android.annotation.SystemApi; +import android.os.Build; import android.os.Handler; +import android.os.SystemClock; import android.util.Log; import android.util.SparseArray; @@ -1555,6 +1558,103 @@ public abstract class SensorManager { Sensor sensor, boolean disable); + /** + * For testing purposes only. Not for third party applications. + * + * Enable data injection mode in sensor service. This mode is + * expected to be used only for testing purposes. If the HAL is + * set to data injection mode, it will ignore the input from + * physical sensors and read sensor data that is injected from + * the test application. This mode is used for testing vendor + * implementations for various algorithms like Rotation Vector, + * Significant Motion, Step Counter etc. + * + * The tests which call this API need to have {@code + * android.permission.HARDWARE_TEST} permission which isn't + * available for third party applications. + * + * @param enable True to set the HAL in DATA_INJECTION mode. + * False to reset the HAL back to NORMAL mode. + * + * @return true if the HAL supports data injection and false + * otherwise. + * @hide + */ + @SystemApi + public boolean enableDataInjectionMode(boolean enable) { + return enableDataInjectionImpl(enable); + } + + /** + * @hide + */ + protected abstract boolean enableDataInjectionImpl(boolean enable); + + /** + * For testing purposes only. Not for third party applications. + * + * This method is used to inject raw sensor data into the HAL. + * Call enableDataInjection before this method to set the HAL in + * data injection mode. This method should be called only if a + * previous call to enableDataInjection has been successful and + * the HAL is already in data injection mode. + * + * The tests which call this API need to have {@code + * android.permission.HARDWARE_TEST} permission which isn't + * available for third party applications. + * + * @param sensor The sensor to inject. + * @param values Sensor values to inject. The length of this + * array must be exactly equal to the number of + * values reported by the sensor type. + * @param accuracy Accuracy of the sensor. + * @param timestamp Sensor timestamp associated with the event. + * + * @return boolean True if the data injection succeeds, false + * otherwise. + * @throws IllegalArgumentException when the sensor is null, + * data injection is not supported by the sensor, values + * are null, incorrect number of values for the sensor, + * sensor accuracy is incorrect or timestamps are + * invalid. + * @hide + */ + @SystemApi + public boolean injectSensorData(Sensor sensor, float[] values, int accuracy, + long timestamp) { + if (sensor == null) { + throw new IllegalArgumentException("sensor cannot be null"); + } + if (!sensor.isDataInjectionSupported()) { + throw new IllegalArgumentException("sensor does not support data injection"); + } + if (values == null) { + throw new IllegalArgumentException("sensor data cannot be null"); + } + int expectedNumValues = Sensor.getMaxLengthValuesArray(sensor, Build.VERSION_CODES.MNC); + if (values.length != expectedNumValues) { + throw new IllegalArgumentException ("Wrong number of values for sensor " + + sensor.getName() + " actual=" + values.length + " expected=" + + expectedNumValues); + } + if (accuracy < SENSOR_STATUS_NO_CONTACT || accuracy > SENSOR_STATUS_ACCURACY_HIGH) { + throw new IllegalArgumentException("Invalid sensor accuracy"); + } + if (timestamp <= 0) { + throw new IllegalArgumentException("Negative or zero sensor timestamp"); + } + if (timestamp > SystemClock.elapsedRealtimeNanos()) { + throw new IllegalArgumentException("Sensor timestamp into the future"); + } + return injectSensorDataImpl(sensor, values, accuracy, timestamp); + } + + /** + * @hide + */ + protected abstract boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy, + long timestamp); + private LegacySensorManager getLegacySensorManager() { synchronized (mSensorListByType) { if (mLegacySensorManager == null) { diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 7ad3a68..11037fd 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -16,7 +16,9 @@ package android.hardware; +import android.Manifest; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; @@ -40,11 +42,14 @@ import java.util.List; public class SystemSensorManager extends SensorManager { private static native void nativeClassInit(); private static native int nativeGetNextSensor(Sensor sensor, int next); + private static native int nativeEnableDataInjection(boolean enable); private static boolean sSensorModuleInitialized = false; private static final Object sSensorModuleLock = new Object(); private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>(); private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>(); + private static InjectEventQueue mInjectEventQueue = null; + private static boolean mDataInjectionMode = false; // Listener list private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners = @@ -56,6 +61,7 @@ public class SystemSensorManager extends SensorManager { private final Looper mMainLooper; private final int mTargetSdkLevel; private final String mPackageName; + private final boolean mHasDataInjectionPermissions; /** {@hide} */ public SystemSensorManager(Context context, Looper mainLooper) { @@ -82,6 +88,8 @@ public class SystemSensorManager extends SensorManager { } } while (i>0); } + mHasDataInjectionPermissions = context.checkSelfPermission( + Manifest.permission.HARDWARE_TEST) == PackageManager.PERMISSION_GRANTED; } } @@ -219,19 +227,72 @@ public class SystemSensorManager extends SensorManager { } } + protected boolean enableDataInjectionImpl(boolean enable) { + if (!mHasDataInjectionPermissions) { + throw new SecurityException("Permission denial. Calling enableDataInjection without " + + Manifest.permission.HARDWARE_TEST); + } + synchronized (sSensorModuleLock) { + int ret = nativeEnableDataInjection(enable); + // The HAL does not support injection. Ignore. + if (ret != 0) { + Log.e(TAG, "HAL does not support data injection"); + return false; + } + mDataInjectionMode = enable; + // If data injection is being disabled clean up the native resources. + if (!enable && mInjectEventQueue != null) { + mInjectEventQueue.dispose(); + mInjectEventQueue = null; + } + return true; + } + } + + protected boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy, + long timestamp) { + if (!mHasDataInjectionPermissions) { + throw new SecurityException("Permission denial. Calling injectSensorData without " + + Manifest.permission.HARDWARE_TEST); + } + synchronized (sSensorModuleLock) { + if (!mDataInjectionMode) { + Log.e(TAG, "Data injection mode not activated before calling injectSensorData"); + return false; + } + if (mInjectEventQueue == null) { + mInjectEventQueue = new InjectEventQueue(mMainLooper, this); + } + int ret = mInjectEventQueue.injectSensorData(sensor.getHandle(), values, accuracy, + timestamp); + // If there are any errors in data injection clean up the native resources. + if (ret != 0) { + mInjectEventQueue.dispose(); + mInjectEventQueue = null; + mDataInjectionMode = false; + } + return ret == 0; + } + } + /* * BaseEventQueue is the communication channel with the sensor service, * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between - * the queues and the listeners. + * the queues and the listeners. InjectEventQueue is also a sub-class which is a special case + * where data is being injected into the sensor HAL through the sensor service. It is not + * associated with any listener and there is one InjectEventQueue associated with a + * SensorManager instance. */ private static abstract class BaseEventQueue { private native long nativeInitBaseEventQueue(WeakReference<BaseEventQueue> eventQWeak, - MessageQueue msgQ, float[] scratch, String packageName); + MessageQueue msgQ, float[] scratch, String packageName, int mode); private static native int nativeEnableSensor(long eventQ, int handle, int rateUs, int maxBatchReportLatencyUs); private static native int nativeDisableSensor(long eventQ, int handle); private static native void nativeDestroySensorEventQueue(long eventQ); private static native int nativeFlushSensor(long eventQ); + private static native int nativeInjectSensorData(long eventQ, int handle, + float[] values,int accuracy, long timestamp); private long nSensorEventQueue; private final SparseBooleanArray mActiveSensors = new SparseBooleanArray(); protected final SparseIntArray mSensorAccuracies = new SparseIntArray(); @@ -240,10 +301,12 @@ public class SystemSensorManager extends SensorManager { private final float[] mScratch = new float[16]; protected final SystemSensorManager mManager; - BaseEventQueue(Looper looper, SystemSensorManager manager) { + protected static final int OPERATING_MODE_NORMAL = 0; + protected static final int OPERATING_MODE_DATA_INJECTION = 1; + + BaseEventQueue(Looper looper, SystemSensorManager manager, int mode) { nSensorEventQueue = nativeInitBaseEventQueue(new WeakReference<BaseEventQueue>(this), - looper.getQueue(), mScratch, - manager.mPackageName); + looper.getQueue(), mScratch, manager.mPackageName, mode); mCloseGuard.open("dispose"); mManager = manager; } @@ -340,6 +403,11 @@ public class SystemSensorManager extends SensorManager { maxBatchReportLatencyUs); } + protected int injectSensorDataBase(int handle, float[] values, int accuracy, + long timestamp) { + return nativeInjectSensorData(nSensorEventQueue, handle, values, accuracy, timestamp); + } + private int disableSensor(Sensor sensor) { if (nSensorEventQueue == 0) throw new NullPointerException(); if (sensor == null) throw new NullPointerException(); @@ -359,7 +427,7 @@ public class SystemSensorManager extends SensorManager { public SensorEventQueue(SensorEventListener listener, Looper looper, SystemSensorManager manager) { - super(looper, manager); + super(looper, manager, OPERATING_MODE_NORMAL); mListener = listener; } @@ -426,7 +494,7 @@ public class SystemSensorManager extends SensorManager { public TriggerEventQueue(TriggerEventListener listener, Looper looper, SystemSensorManager manager) { - super(looper, manager); + super(looper, manager, OPERATING_MODE_NORMAL); mListener = listener; } @@ -477,4 +545,34 @@ public class SystemSensorManager extends SensorManager { protected void dispatchFlushCompleteEvent(int handle) { } } + + static final class InjectEventQueue extends BaseEventQueue { + public InjectEventQueue(Looper looper, SystemSensorManager manager) { + super(looper, manager, OPERATING_MODE_DATA_INJECTION); + } + + int injectSensorData(int handle, float[] values,int accuracy, long timestamp) { + return injectSensorDataBase(handle, values, accuracy, timestamp); + } + + @SuppressWarnings("unused") + protected void dispatchSensorEvent(int handle, float[] values, int accuracy, + long timestamp) { + } + + @SuppressWarnings("unused") + protected void dispatchFlushCompleteEvent(int handle) { + + } + + @SuppressWarnings("unused") + protected void addSensorEvent(Sensor sensor) { + + } + + @SuppressWarnings("unused") + protected void removeSensorEvent(Sensor sensor) { + + } + } } diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index 691798f..a4d6be0 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 481fc2f..1b57055 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/os/Parcel.java b/core/java/android/os/Parcel.java index 43309c0..8c1f44f 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -115,15 +115,15 @@ 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 0b1031c..16e0bf7 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -16,6 +16,7 @@ package android.os.storage; +import android.content.pm.IPackageMoveObserver; import android.os.Binder; import android.os.IBinder; import android.os.IInterface; @@ -1082,12 +1083,14 @@ 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 747fb40..6116aef 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -22,6 +22,8 @@ import android.annotation.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/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index cc7f880..35b8819 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -358,6 +358,20 @@ 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 0417921..4d1209a 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -16,7 +16,6 @@ package android.view.animation; -import android.content.res.Configuration; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -45,16 +44,6 @@ 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/GridView.java b/core/java/android/widget/GridView.java index 9ecdc9c..c959774 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1854,20 +1854,19 @@ 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 a50941b..10e4db3 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/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java index f7e9648..c0c8aec 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -403,7 +403,7 @@ 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/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 16e5b3c..0cf596c 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -174,6 +174,11 @@ nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next) return size_t(next) < count ? next : 0; } +static int nativeEnableDataInjection(JNIEnv *_env, jclass _this, jboolean enable) { + SensorManager& mgr(SensorManager::getInstance()); + return mgr.enableDataInjection(enable); +} + //---------------------------------------------------------------------------- class Receiver : public LooperCallback { @@ -277,11 +282,11 @@ private: }; static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQWeak, jobject msgQ, - jfloatArray scratch, jstring packageName) { + jfloatArray scratch, jstring packageName, jint mode) { SensorManager& mgr(SensorManager::getInstance()); ScopedUtfChars packageUtf(env, packageName); String8 clientName(packageUtf.c_str()); - sp<SensorEventQueue> queue(mgr.createEventQueue(clientName)); + sp<SensorEventQueue> queue(mgr.createEventQueue(clientName, mode)); sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ); if (messageQueue == NULL) { @@ -297,7 +302,6 @@ static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject event static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, jint rate_us, jint maxBatchReportLatency) { sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); - return receiver->getSensorEventQueue()->enableSensor(handle, rate_us, maxBatchReportLatency, 0); } @@ -307,7 +311,7 @@ static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint ha return receiver->getSensorEventQueue()->disableSensor(handle); } -static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jlong eventQ, jint handle) { +static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jlong eventQ) { sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); receiver->destroy(); receiver->decStrong((void*)nativeInitSensorEventQueue); @@ -318,6 +322,17 @@ static jint nativeFlushSensor(JNIEnv *env, jclass clazz, jlong eventQ) { return receiver->getSensorEventQueue()->flush(); } +static jint nativeInjectSensorData(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, + jfloatArray values, jint accuracy, jlong timestamp) { + sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ)); + // Create a sensor_event from the above data which can be injected into the HAL. + ASensorEvent sensor_event; + memset(&sensor_event, 0, sizeof(sensor_event)); + sensor_event.sensor = handle; + sensor_event.timestamp = timestamp; + env->GetFloatArrayRegion(values, 0, env->GetArrayLength(values), sensor_event.data); + return receiver->getSensorEventQueue()->injectSensorEvent(sensor_event); +} //---------------------------------------------------------------------------- static JNINativeMethod gSystemSensorManagerMethods[] = { @@ -328,11 +343,15 @@ static JNINativeMethod gSystemSensorManagerMethods[] = { {"nativeGetNextSensor", "(Landroid/hardware/Sensor;I)I", (void*)nativeGetNextSensor }, + + {"nativeEnableDataInjection", + "(Z)I", + (void*)nativeEnableDataInjection }, }; static JNINativeMethod gBaseEventQueueMethods[] = { {"nativeInitBaseEventQueue", - "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;)J", + "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;I)J", (void*)nativeInitSensorEventQueue }, {"nativeEnableSensor", @@ -350,6 +369,10 @@ static JNINativeMethod gBaseEventQueueMethods[] = { {"nativeFlushSensor", "(J)I", (void*)nativeFlushSensor }, + + {"nativeInjectSensorData", + "(JI[FIJ)I", + (void*)nativeInjectSensorData }, }; }; // namespace android diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 8b2c269..2f6a69c 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -189,32 +189,44 @@ 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 4f26eb2..018c1a1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2106,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/list_highlight_material.xml b/core/res/res/drawable/list_choice_background_material.xml index 5a930c4..5a930c4 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/values/attrs.xml b/core/res/res/values/attrs.xml index 00c771d..887b0a5 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6038,19 +6038,8 @@ <!-- values are colors, which are integers starting with "#". --> <enum name="colorType" value="3" /> </attr> - <!-- Defines whether the animation should adjust duration in order to achieve the same - perceived effects on different devices. --> - <attr name="durationScaleHint" > - <!-- Default value for scale hint. When set, duration will not be scaled.--> - <enum name="noScale" value="0"/> - <!-- This should be used when the animation's moving distance is proportional to screen, - as the scaling is based on screen size. --> - <enum name="screenBased" value="1"/> - <!-- This is for animations that have a distance defined in dp, which will be the same - across different devices. In this case, scaling is based on the physical distance - per dp on the current device. --> - <enum name="dpBased" value="2"/> - </attr> + <!-- Placeholder for a deleted attribute. This should be removed before M release. --> + <attr name="removeBeforeMRelease" format="integer" /> </declare-styleable> <declare-styleable name="PropertyValuesHolder"> @@ -6099,7 +6088,6 @@ <!-- child animations should be played sequentially, in the same order as the xml. --> <enum name="sequentially" value="1" /> </attr> - <attr name="durationScaleHint" /> </declare-styleable> <!-- ========================== --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index baccafd..875659d 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2664,8 +2664,8 @@ <public type="attr" name="supportsAssistGesture" /> <public type="attr" name="thumbPosition" /> - <!-- Animation --> - <public type="attr" name="durationScaleHint" /> + <!-- Placeholder for a removed attribute. Remove this before M release. --> + <public type="attr" name="removeBeforeMRelease" /> <public type="attr" name="lockTaskMode" /> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index db178fa..f81ee8c 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -614,7 +614,7 @@ 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/themes_material.xml b/core/res/res/values/themes_material.xml index a413d91..e8aab07 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -129,8 +129,8 @@ 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 d674f7f..5ccdcb9 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/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java index 5fae831..3b25ba6 100644 --- a/keystore/java/android/security/AndroidKeyPairGenerator.java +++ b/keystore/java/android/security/AndroidKeyPairGenerator.java @@ -17,7 +17,7 @@ package android.security; import com.android.org.bouncycastle.x509.X509V3CertificateGenerator; -import com.android.org.conscrypt.NativeCrypto; +import com.android.org.conscrypt.NativeConstants; import com.android.org.conscrypt.OpenSSLEngine; import java.security.InvalidAlgorithmParameterException; @@ -206,9 +206,9 @@ 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 1c9d005..0000000 --- a/keystore/java/android/security/CryptoOperationException.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security; - -/** - * Base class for exceptions during cryptographic operations which cannot throw a suitable checked - * exception. - * - * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or - * {@code Signature}) is that they can throw a checked exception during initialization, but are not - * permitted to throw a checked exception during operation. Because crypto operations can fail - * for a variety of reasons after initialization, this base class provides type-safety for unchecked - * exceptions that may be thrown in those cases. - */ -public class CryptoOperationException extends RuntimeException { - - /** - * Constructs a new {@code CryptoOperationException} without detail message and cause. - */ - public CryptoOperationException() { - super(); - } - - /** - * Constructs a new {@code CryptoOperationException} with the provided detail message and no - * cause. - */ - public CryptoOperationException(String message) { - super(message); - } - - /** - * Constructs a new {@code CryptoOperationException} with the provided detail message and cause. - */ - public CryptoOperationException(String message, Throwable cause) { - super(message, cause); - } - - /** - * Constructs a new {@code CryptoOperationException} with the provided cause. - */ - public CryptoOperationException(Throwable cause) { - super(cause); - } -} diff --git a/keystore/java/android/security/KeyExpiredException.java b/keystore/java/android/security/KeyExpiredException.java index a02dc33..f58e48a 100644 --- a/keystore/java/android/security/KeyExpiredException.java +++ b/keystore/java/android/security/KeyExpiredException.java @@ -16,11 +16,13 @@ package android.security; +import java.security.InvalidKeyException; + /** * Indicates that a cryptographic operation failed because the employed key's validity end date * is in the past. */ -public class KeyExpiredException extends CryptoOperationException { +public class KeyExpiredException extends InvalidKeyException { /** * Constructs a new {@code KeyExpiredException} without detail message and cause. diff --git a/keystore/java/android/security/KeyNotYetValidException.java b/keystore/java/android/security/KeyNotYetValidException.java index 964cd7e..4ea27ef 100644 --- a/keystore/java/android/security/KeyNotYetValidException.java +++ b/keystore/java/android/security/KeyNotYetValidException.java @@ -16,11 +16,13 @@ package android.security; +import java.security.InvalidKeyException; + /** * Indicates that a cryptographic operation failed because the employed key's validity start date * is in the future. */ -public class KeyNotYetValidException extends CryptoOperationException { +public class KeyNotYetValidException extends InvalidKeyException { /** * Constructs a new {@code KeyNotYetValidException} without detail message and cause. diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 5af0527..8c49ff0 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -16,7 +16,7 @@ package android.security; -import com.android.org.conscrypt.NativeCrypto; +import com.android.org.conscrypt.NativeConstants; import android.os.Binder; import android.os.IBinder; @@ -30,6 +30,7 @@ import android.security.keymaster.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 1f8d8ec..3b13e83 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, @@ -158,7 +166,11 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry try { init(opmode, key, random); initAlgorithmSpecificParameters(); - ensureKeystoreOperationInitialized(); + try { + ensureKeystoreOperationInitialized(); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidKeyException(e); + } success = true; } finally { if (!success) { @@ -236,6 +248,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry mOperationToken = null; mOperationHandle = null; mMainDataStreamer = null; + mCachedException = null; } private void resetWhilePreservingInitState() { @@ -247,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"); } @@ -281,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; @@ -299,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; @@ -309,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) { @@ -338,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 { @@ -352,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); } } @@ -613,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 8ed6e04..1aa3aec 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 aafd2fa..0619199 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 f8b6fef..175369c 100644 --- a/keystore/java/android/security/KeyStoreHmacSpi.java +++ b/keystore/java/android/security/KeyStoreHmacSpi.java @@ -147,7 +147,7 @@ public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOp resetWhilePreservingInitState(); } - private void ensureKeystoreOperationInitialized() { + private void ensureKeystoreOperationInitialized() throws InvalidKeyException { if (mChunkedStreamer != null) { return; } @@ -169,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; @@ -188,28 +188,36 @@ 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); } resetWhilePreservingInitState(); diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java index d1abe12..293c4c9 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -210,7 +210,8 @@ 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 806b214..4fe210b 100644 --- a/keystore/java/android/security/NewFingerprintEnrolledException.java +++ b/keystore/java/android/security/NewFingerprintEnrolledException.java @@ -16,11 +16,13 @@ package android.security; +import java.security.InvalidKeyException; + /** * Indicates that a cryptographic operation could not be performed because the key used by the * operation is permanently invalid because a new fingerprint was enrolled. */ -public class NewFingerprintEnrolledException extends CryptoOperationException { +public class NewFingerprintEnrolledException extends InvalidKeyException { /** * Constructs a new {@code NewFingerprintEnrolledException} without detail message and cause. diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java index f5f5f41..66f4dd8 100644 --- a/keystore/java/android/security/UserNotAuthenticatedException.java +++ b/keystore/java/android/security/UserNotAuthenticatedException.java @@ -16,11 +16,13 @@ package android.security; +import java.security.InvalidKeyException; + /** * Indicates that a cryptographic operation could not be performed because the user has not been * authenticated recently enough. */ -public class UserNotAuthenticatedException extends CryptoOperationException { +public class UserNotAuthenticatedException extends InvalidKeyException { /** * Constructs a new {@code UserNotAuthenticatedException} without detail message and cause. diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java index 7a88dee..a7046dd 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 1a5552a..916b1ba 100644 --- a/keystore/tests/src/android/security/KeyStoreTest.java +++ b/keystore/tests/src/android/security/KeyStoreTest.java @@ -32,7 +32,7 @@ import android.test.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/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index ff6fed2..a7e092f 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -55,16 +55,16 @@ public 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 cb70e8b..d851ad7 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/AudioTrack.java b/media/java/android/media/AudioTrack.java index 3577357..6f1fd24 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -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); } //-------------- @@ -973,8 +926,7 @@ public class AudioTrack return new PlaybackSettings() .setSpeed(floatArray[0]) .setPitch(floatArray[1]) - .setAudioFallbackMode(intArray[0]) - .setAudioStretchMode(intArray[1]); + .setAudioFallbackMode(intArray[0]); } /** @@ -1397,7 +1349,7 @@ public class AudioTrack }; intArray = new int[] { settings.getAudioFallbackMode(), - settings.getAudioStretchMode(), + PlaybackSettings.AUDIO_STRETCH_MODE_DEFAULT, }; } catch (IllegalStateException e) { throw new IllegalArgumentException(e); @@ -2362,7 +2314,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 75901fd..195c987 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 e54525d..6d30208 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 c18b463..f805339 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 1f00c7b..d22cfda 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 3b4f8e5..dc6760d 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/PlaybackSettings.java b/media/java/android/media/PlaybackSettings.java index ceb6bb1..b2e1033 100644 --- a/media/java/android/media/PlaybackSettings.java +++ b/media/java/android/media/PlaybackSettings.java @@ -38,14 +38,6 @@ import android.annotation.IntDef; * Return {@link java.lang.IllegalArgumentException} from * <code>AudioTrack.setPlaybackSettings(PlaybackSettings)</code>.</li> * </ul> - * <p> <strong>audio stretch mode:</strong> select - * timestretch handling. - * <ul> - * <li> {@link PlaybackSettings#AUDIO_STRETCH_MODE_DEFAULT}: - * System will determine best selection. </li> - * <li> {@link PlaybackSettings#AUDIO_STRETCH_MODE_VOICE}: - * Content is primarily voice.</li> - * </ul> * <p> <strong>pitch:</strong> increases or decreases the tonal frequency of the audio content. * It is expressed as a multiplicative factor, where normal pitch is 1.0f. * <p> <strong>speed:</strong> increases or decreases the time to @@ -84,7 +76,9 @@ public final class PlaybackSettings { ) @Retention(RetentionPolicy.SOURCE) public @interface AudioStretchMode {} + /** @hide */ public static final int AUDIO_STRETCH_MODE_DEFAULT = 0; + /** @hide */ public static final int AUDIO_STRETCH_MODE_VOICE = 1; // flags to indicate which settings are actually set @@ -136,6 +130,7 @@ public final class PlaybackSettings { } /** + * @hide * Sets the audio stretch mode. * @param audioStretchMode * @return this <code>PlaybackSettings</code> instance. @@ -147,6 +142,7 @@ public final class PlaybackSettings { } /** + * @hide * Retrieves the audio stretch mode. * @return audio stretch mode * @throws IllegalStateException if the audio stretch mode is not set. diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java index 754facd..966e41a 100644 --- a/media/java/android/media/tv/TvContentRating.java +++ b/media/java/android/media/tv/TvContentRating.java @@ -16,9 +16,12 @@ package android.media.tv; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.text.TextUtils; +import com.android.internal.util.Preconditions; + import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -693,6 +696,12 @@ 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/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 0f265de..601fa45 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -40,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; @@ -934,9 +936,7 @@ public final class TvInputManager { */ @Nullable public TvInputInfo getTvInputInfo(@NonNull String inputId) { - if (inputId == null) { - throw new IllegalArgumentException("inputId cannot be null"); - } + Preconditions.checkNotNull(inputId); try { return mService.getTvInputInfo(inputId, mUserId); } catch (RemoteException e) { @@ -958,9 +958,7 @@ public final class TvInputManager { * @throws IllegalArgumentException if the argument is {@code null}. */ public int getInputState(@NonNull String inputId) { - if (inputId == null) { - throw new IllegalArgumentException("inputId cannot be null"); - } + Preconditions.checkNotNull(inputId); synchronized (mLock) { Integer state = mStateMap.get(inputId); if (state == null) { @@ -976,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(@NonNull TvInputCallback callback, @NonNull Handler handler) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - if (handler == null) { - throw new IllegalArgumentException("handler cannot be null"); - } + Preconditions.checkNotNull(callback); + Preconditions.checkNotNull(handler); synchronized (mLock) { mCallbackRecords.add(new TvInputCallbackRecord(callback, handler)); } @@ -994,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(@NonNull final TvInputCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } + Preconditions.checkNotNull(callback); synchronized (mLock) { for (Iterator<TvInputCallbackRecord> it = mCallbackRecords.iterator(); it.hasNext(); ) { @@ -1049,9 +1039,7 @@ public final class TvInputManager { * @return {@code true} if the given TV content rating is blocked, {@code false} otherwise. */ public boolean isRatingBlocked(@NonNull TvContentRating rating) { - if (rating == null) { - throw new IllegalArgumentException("rating cannot be null"); - } + Preconditions.checkNotNull(rating); try { return mService.isRatingBlocked(rating.flattenToString(), mUserId); } catch (RemoteException e) { @@ -1087,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) { @@ -1107,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) { @@ -1140,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++; @@ -1436,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); @@ -1447,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; @@ -1790,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"); } @@ -1818,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; @@ -1853,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) { @@ -1871,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 4258534..5156ae8 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -51,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; @@ -313,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 cannot be null"); - } + public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) { + Preconditions.checkNotNull(eventType); executeOrPostRunnable(new Runnable() { @Override public void run() { @@ -546,9 +545,7 @@ public abstract class TvInputService extends Service { * @see TvInputManager */ public void notifyContentBlocked(@NonNull final TvContentRating rating) { - if (rating == null) { - throw new IllegalArgumentException("rating cannot be null"); - } + Preconditions.checkNotNull(rating); executeOrPostRunnable(new Runnable() { @Override public void run() { diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java index 6eedeb4..2c956e9 100644 --- a/media/java/android/media/tv/TvTrackInfo.java +++ b/media/java/android/media/tv/TvTrackInfo.java @@ -16,10 +16,13 @@ package android.media.tv; +import android.annotation.NonNull; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + /** * Encapsulates the format of tracks played in {@link TvInputService}. */ @@ -245,15 +248,13 @@ 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/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index d2c614e..8c76665 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -338,7 +338,7 @@ 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/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java index e0af29d..1cf7248 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java @@ -32,6 +32,7 @@ import android.os.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 35e9636..5b4b4fd 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -94,6 +94,7 @@ <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/> <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" /> + <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" /> <application android:label="@string/app_label"> <provider diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 292c9c2..0d331d1 100755 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -328,7 +328,7 @@ 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 b828e78..7b555fc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1549,6 +1549,7 @@ 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 de4874f..e542264 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -700,6 +700,26 @@ 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 c854d63..d058892 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 95d0b42..c94623a 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/MountService.java b/services/core/java/com/android/server/MountService.java index 89a7173..7172ab7 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -30,6 +30,8 @@ import android.content.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 1b32f57..999e91b 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -734,12 +734,15 @@ 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/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index 28597c1..6b5908d 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -37,7 +37,6 @@ import 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 1008653..25998da 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -36,6 +36,9 @@ import android.app.NotificationManager; 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 5569a09..e106a4a 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -50,6 +50,8 @@ 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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bd22524..f087c33 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -14307,12 +14307,22 @@ public 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 25857c5..5536d4d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2151,6 +2151,16 @@ 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/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 897b865..52071cc 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -201,9 +201,11 @@ 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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6fc3103..2b88158 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -771,8 +771,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { permittedInputMethods = readPackageList(parser, tag); } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); + XmlUtils.skipCurrentTag(parser); } - XmlUtils.skipCurrentTag(parser); } } @@ -1565,11 +1565,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("failed-password-attempts".equals(tag)) { policy.mFailedPasswordAttempts = Integer.parseInt( parser.getAttributeValue(null, "value")); - XmlUtils.skipCurrentTag(parser); } else if ("password-owner".equals(tag)) { policy.mPasswordOwner = Integer.parseInt( parser.getAttributeValue(null, "value")); - XmlUtils.skipCurrentTag(parser); } else if ("active-password".equals(tag)) { policy.mActivePasswordQuality = Integer.parseInt( parser.getAttributeValue(null, "quality")); @@ -1587,14 +1585,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { parser.getAttributeValue(null, "symbols")); policy.mActivePasswordNonLetter = Integer.parseInt( parser.getAttributeValue(null, "nonletter")); - XmlUtils.skipCurrentTag(parser); } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) { policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name")); - XmlUtils.skipCurrentTag(parser); } else if (TAG_STATUS_BAR.equals(tag)) { policy.mStatusBarEnabledState = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_ENABLED)); - XmlUtils.skipCurrentTag(parser); } else if (DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML.equals(tag)) { policy.doNotAskCredentialsOnBoot = true; } else { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 3d54dfb..04984d3 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -17,7 +17,10 @@ package com.android.server.usage; import android.Manifest; +import android.app.ActivityManagerNative; +import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.admin.DevicePolicyManager; import android.app.usage.ConfigurationStats; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; @@ -25,11 +28,13 @@ import android.app.usage.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 0a9481a..d94759d 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -211,6 +211,17 @@ 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 8f0c6c8..daccf95 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -142,8 +142,10 @@ 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/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java index eef72fb..bf8fac6 100644 --- a/telecomm/java/android/telecom/DefaultDialerManager.java +++ b/telecomm/java/android/telecom/DefaultDialerManager.java @@ -87,15 +87,15 @@ 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/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index fd95327..8d6bda8 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -110,6 +110,28 @@ 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/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 45b2482..49f2aad 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -137,6 +137,16 @@ interface ITelecomService { */ ComponentName getDefaultPhoneApp(); + /** + * @see TelecomServiceImpl#getDefaultDialerPackage + */ + String getDefaultDialerPackage(); + + /** + * @see TelecomServiceImpl#getSystemDialerPackage + */ + String getSystemDialerPackage(); + // // Internal system apis relating to call management. // diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 7d1a2fa..831a194 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -208,13 +208,13 @@ 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/tools/aapt2/BinaryResourceParser.cpp b/tools/aapt2/BinaryResourceParser.cpp index 71016c1..326a2ac 100644 --- a/tools/aapt2/BinaryResourceParser.cpp +++ b/tools/aapt2/BinaryResourceParser.cpp @@ -603,6 +603,13 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na mTable->getValueStringPool().makeRef( styleStr, StringPool::Context{1, config})); } else { + if (name.type != ResourceType::kString && + util::stringStartsWith<char16_t>(str, u"res/")) { + // This must be a FileReference. + return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef( + str, StringPool::Context{ 0, config })); + } + // There are no styles associated with this string, so treat it as // a simple string. return util::make_unique<String>( diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 03b9ba4..be806c9 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -25,6 +25,7 @@ #include "Linker.h" #include "ManifestParser.h" #include "ManifestValidator.h" +#include "NameMangler.h" #include "Png.h" #include "ResourceParser.h" #include "ResourceTable.h" @@ -266,22 +267,50 @@ struct CompileItem { struct LinkItem { Source source; - std::string apkPath; + ResourceName name; + ConfigDescription config; + std::string originalPath; + ZipFile* apk; }; -std::string buildFileReference(const CompileItem& item) { +template <typename TChar> +static BasicStringPiece<TChar> getExtension(const BasicStringPiece<TChar>& str) { + auto iter = std::find(str.begin(), str.end(), static_cast<TChar>('.')); + if (iter == str.end()) { + return BasicStringPiece<TChar>(); + } + size_t offset = (iter - str.begin()) + 1; + return str.substr(offset, str.size() - offset); +} + + + +std::string buildFileReference(const ResourceNameRef& name, const ConfigDescription& config, + const StringPiece& extension) { std::stringstream path; - path << "res/" << item.name.type; - if (item.config != ConfigDescription{}) { - path << "-" << item.config; + path << "res/" << name.type; + if (config != ConfigDescription{}) { + path << "-" << config; + } + path << "/" << util::utf16ToUtf8(name.entry); + if (!extension.empty()) { + path << "." << extension; } - path << "/" << util::utf16ToUtf8(item.name.entry) + "." + item.extension; return path.str(); } +std::string buildFileReference(const CompileItem& item) { + return buildFileReference(item.name, item.config, item.extension); +} + +std::string buildFileReference(const LinkItem& item) { + return buildFileReference(item.name, item.config, getExtension<char>(item.originalPath)); +} + bool addFileReference(const std::shared_ptr<ResourceTable>& table, const CompileItem& item) { StringPool& pool = table->getValueStringPool(); - StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item))); + StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)), + StringPool::Context{ 0, item.config }); return table->addResource(item.name, item.config, item.source.line(0), util::make_unique<FileReference>(ref)); } @@ -418,8 +447,8 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<Resolver>& resolv return false; } - if (outApk->add(outBuffer, item.apkPath.data(), ZipEntry::kCompressDeflated, nullptr) != - android::NO_ERROR) { + if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressDeflated, + nullptr) != android::NO_ERROR) { Logger::error(options.output) << "failed to write linked file '" << item.source << "' to apk." << std::endl; return false; @@ -502,109 +531,6 @@ bool compileManifest(const AaptOptions& options, const std::shared_ptr<Resolver> return true; } -bool loadAppInfo(const Source& source, AppInfo* outInfo) { - std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary); - if (!ifs) { - Logger::error(source) << strerror(errno) << std::endl; - return false; - } - - ManifestParser parser; - std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs); - return parser.parse(source, pullParser, outInfo); -} - -static void printCommandsAndDie() { - std::cerr << "The following commands are supported:" << std::endl << std::endl; - std::cerr << "compile compiles a subset of resources" << std::endl; - std::cerr << "link links together compiled resources and libraries" << std::endl; - std::cerr << std::endl; - std::cerr << "run aapt2 with one of the commands and the -h flag for extra details." - << std::endl; - exit(1); -} - -static AaptOptions prepareArgs(int argc, char** argv) { - if (argc < 2) { - std::cerr << "no command specified." << std::endl << std::endl; - printCommandsAndDie(); - } - - const StringPiece command(argv[1]); - argc -= 2; - argv += 2; - - AaptOptions options; - - if (command == "--version" || command == "version") { - std::cout << kAaptVersionStr << std::endl; - exit(0); - } else if (command == "link") { - options.phase = AaptOptions::Phase::Link; - } else if (command == "compile") { - options.phase = AaptOptions::Phase::Compile; - } else { - std::cerr << "invalid command '" << command << "'." << std::endl << std::endl; - printCommandsAndDie(); - } - - if (options.phase == AaptOptions::Phase::Compile) { - flag::requiredFlag("--package", "Android package name", - [&options](const StringPiece& arg) { - options.appInfo.package = util::utf8ToUtf16(arg); - }); - flag::optionalFlag("--binding", "Output directory for binding XML files", - [&options](const StringPiece& arg) { - options.bindingOutput = Source{ arg.toString() }; - }); - flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning", - false, &options.versionStylesAndLayouts); - - } else if (options.phase == AaptOptions::Phase::Link) { - flag::requiredFlag("--manifest", "AndroidManifest.xml of your app", - [&options](const StringPiece& arg) { - options.manifest = Source{ arg.toString() }; - }); - - flag::optionalFlag("-I", "add an Android APK to link against", - [&options](const StringPiece& arg) { - options.libraries.push_back(Source{ arg.toString() }); - }); - - flag::optionalFlag("--java", "directory in which to generate R.java", - [&options](const StringPiece& arg) { - options.generateJavaClass = Source{ arg.toString() }; - }); - } - - // Common flags for all steps. - flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) { - options.output = Source{ arg.toString() }; - }); - - bool help = false; - flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose); - flag::optionalSwitch("-h", "displays this help menu", true, &help); - - // Build the command string for output (eg. "aapt2 compile"). - std::string fullCommand = "aapt2"; - fullCommand += " "; - fullCommand += command.toString(); - - // Actually read the command line flags. - flag::parse(argc, argv, fullCommand); - - if (help) { - flag::usageAndDie(fullCommand); - } - - // Copy all the remaining arguments. - for (const std::string& arg : flag::getArgs()) { - options.input.push_back(Source{ arg }); - } - return options; -} - static bool compileValues(const std::shared_ptr<ResourceTable>& table, const Source& source, const ConfigDescription& config) { std::ifstream in(source.path, std::ifstream::binary); @@ -630,6 +556,7 @@ struct ResourcePathData { * [--/res/]type[-config]/name */ static Maybe<ResourcePathData> extractResourcePathData(const Source& source) { + // TODO(adamlesinski): Use Windows path separator on windows. std::vector<std::string> parts = util::splitAndLowercase(source.path, '/'); if (parts.size() < 2) { Logger::error(source) << "bad resource path." << std::endl; @@ -695,6 +622,38 @@ bool writeResourceTable(const AaptOptions& options, const std::shared_ptr<Resour return true; } +/** + * For each FileReference in the table, adds a LinkItem to the link queue for processing. + */ +static void addApkFilesToLinkQueue(const std::u16string& package, const Source& source, + const std::shared_ptr<ResourceTable>& table, + const std::unique_ptr<ZipFile>& apk, + std::queue<LinkItem>* outLinkQueue) { + bool mangle = package != table->getPackage(); + for (auto& type : *table) { + for (auto& entry : type->entries) { + ResourceName name = { package, type->type, entry->name }; + if (mangle) { + NameMangler::mangle(table->getPackage(), &name.entry); + } + + for (auto& value : entry->values) { + visitFunc<FileReference>(*value.value, [&](FileReference& ref) { + std::string pathUtf8 = util::utf16ToUtf8(*ref.path); + outLinkQueue->push(LinkItem{ + source, name, value.config, pathUtf8, apk.get() }); + // Now rewrite the file path. + if (mangle) { + ref.path = table->getValueStringPool().makeRef(util::utf8ToUtf16( + buildFileReference(name, value.config, + getExtension<char>(pathUtf8)))); + } + }); + } + } + } +} + static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate | ZipFile::kOpenReadWrite; @@ -740,9 +699,14 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT linkedPackages.insert(table->getPackage()); } + std::queue<LinkItem> linkQueue; for (auto& p : apkFiles) { const std::shared_ptr<ResourceTable>& inTable = p.first; + // Collect all FileReferences and add them to the queue for processing. + addApkFilesToLinkQueue(options.appInfo.package, Source{}, inTable, p.second, &linkQueue); + + // Merge the tables. if (!outTable->merge(std::move(*inTable))) { return false; } @@ -779,39 +743,32 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT return false; } - for (auto& p : apkFiles) { - std::unique_ptr<ZipFile>& zipFile = p.second; + for (; !linkQueue.empty(); linkQueue.pop()) { + const LinkItem& item = linkQueue.front(); - // TODO(adamlesinski): Get list of files to read when processing config filter. + ZipEntry* entry = item.apk->getEntryByName(item.originalPath.data()); + if (!entry) { + Logger::error(item.source) << "failed to find '" << item.originalPath << "'." + << std::endl; + return false; + } - const int numEntries = zipFile->getNumEntries(); - for (int i = 0; i < numEntries; i++) { - ZipEntry* entry = zipFile->getEntryByIndex(i); - assert(entry); + if (util::stringEndsWith<char>(item.originalPath, ".xml")) { + void* uncompressedData = item.apk->uncompress(entry); + assert(uncompressedData); - StringPiece filename = entry->getFileName(); - if (!util::stringStartsWith<char>(filename, "res/")) { - continue; + if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(), + &outApk)) { + Logger::error(options.output) << "failed to link '" << item.originalPath << "'." + << std::endl; + return false; } - - if (util::stringEndsWith<char>(filename, ".xml")) { - void* uncompressedData = zipFile->uncompress(entry); - assert(uncompressedData); - - LinkItem item = { Source{ filename.toString() }, filename.toString() }; - - if (!linkXml(options, resolver, item, uncompressedData, - entry->getUncompressedLen(), &outApk)) { - Logger::error(options.output) << "failed to link '" << filename << "'." - << std::endl; - return false; - } - } else { - if (outApk.add(zipFile.get(), entry, 0, nullptr) != android::NO_ERROR) { - Logger::error(options.output) << "failed to copy '" << filename << "'." - << std::endl; - return false; - } + } else { + if (outApk.add(item.apk, entry, buildFileReference(item).data(), 0, nullptr) != + android::NO_ERROR) { + Logger::error(options.output) << "failed to copy '" << item.originalPath << "'." + << std::endl; + return false; } } } @@ -957,6 +914,109 @@ bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t return true; } +bool loadAppInfo(const Source& source, AppInfo* outInfo) { + std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary); + if (!ifs) { + Logger::error(source) << strerror(errno) << std::endl; + return false; + } + + ManifestParser parser; + std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs); + return parser.parse(source, pullParser, outInfo); +} + +static void printCommandsAndDie() { + std::cerr << "The following commands are supported:" << std::endl << std::endl; + std::cerr << "compile compiles a subset of resources" << std::endl; + std::cerr << "link links together compiled resources and libraries" << std::endl; + std::cerr << std::endl; + std::cerr << "run aapt2 with one of the commands and the -h flag for extra details." + << std::endl; + exit(1); +} + +static AaptOptions prepareArgs(int argc, char** argv) { + if (argc < 2) { + std::cerr << "no command specified." << std::endl << std::endl; + printCommandsAndDie(); + } + + const StringPiece command(argv[1]); + argc -= 2; + argv += 2; + + AaptOptions options; + + if (command == "--version" || command == "version") { + std::cout << kAaptVersionStr << std::endl; + exit(0); + } else if (command == "link") { + options.phase = AaptOptions::Phase::Link; + } else if (command == "compile") { + options.phase = AaptOptions::Phase::Compile; + } else { + std::cerr << "invalid command '" << command << "'." << std::endl << std::endl; + printCommandsAndDie(); + } + + if (options.phase == AaptOptions::Phase::Compile) { + flag::requiredFlag("--package", "Android package name", + [&options](const StringPiece& arg) { + options.appInfo.package = util::utf8ToUtf16(arg); + }); + flag::optionalFlag("--binding", "Output directory for binding XML files", + [&options](const StringPiece& arg) { + options.bindingOutput = Source{ arg.toString() }; + }); + flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning", + false, &options.versionStylesAndLayouts); + + } else if (options.phase == AaptOptions::Phase::Link) { + flag::requiredFlag("--manifest", "AndroidManifest.xml of your app", + [&options](const StringPiece& arg) { + options.manifest = Source{ arg.toString() }; + }); + + flag::optionalFlag("-I", "add an Android APK to link against", + [&options](const StringPiece& arg) { + options.libraries.push_back(Source{ arg.toString() }); + }); + + flag::optionalFlag("--java", "directory in which to generate R.java", + [&options](const StringPiece& arg) { + options.generateJavaClass = Source{ arg.toString() }; + }); + } + + // Common flags for all steps. + flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) { + options.output = Source{ arg.toString() }; + }); + + bool help = false; + flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose); + flag::optionalSwitch("-h", "displays this help menu", true, &help); + + // Build the command string for output (eg. "aapt2 compile"). + std::string fullCommand = "aapt2"; + fullCommand += " "; + fullCommand += command.toString(); + + // Actually read the command line flags. + flag::parse(argc, argv, fullCommand); + + if (help) { + flag::usageAndDie(fullCommand); + } + + // Copy all the remaining arguments. + for (const std::string& arg : flag::getArgs()) { + options.input.push_back(Source{ arg }); + } + return options; +} + int main(int argc, char** argv) { Logger::setLog(std::make_shared<Log>(std::cerr, std::cerr)); AaptOptions options = prepareArgs(argc, argv); diff --git a/tools/aapt2/TableFlattener.cpp b/tools/aapt2/TableFlattener.cpp index 67c56e7..4aadadc 100644 --- a/tools/aapt2/TableFlattener.cpp +++ b/tools/aapt2/TableFlattener.cpp @@ -43,8 +43,7 @@ struct FlatEntry { */ class MapFlattener : public ConstValueVisitor { public: - MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, - std::vector<std::pair<ResourceNameRef, uint32_t>>& symbols) : + MapFlattener(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols) : mOut(out), mSymbols(symbols) { mMap = mOut->nextBlock<android::ResTable_map_entry>(); mMap->key.index = flatEntry.entryKey; @@ -65,7 +64,7 @@ public: void flattenParent(const Reference& ref) { if (!ref.id.isValid()) { - mSymbols.push_back({ + mSymbols->push_back({ ResourceNameRef(ref.name), (mOut->size() - mMap->size) + sizeof(*mMap) - sizeof(android::ResTable_entry) }); @@ -80,7 +79,7 @@ public: // Write the key. if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) { - mSymbols.push_back(std::make_pair(ResourceNameRef(key.name), + mSymbols->push_back(std::make_pair(ResourceNameRef(key.name), mOut->size() - sizeof(*outMapEntry))); } outMapEntry->name.ident = key.id.id; @@ -90,7 +89,7 @@ public: if (outMapEntry->value.data == 0x0) { visitFunc<Reference>(value, [&](const Reference& reference) { - mSymbols.push_back(std::make_pair(ResourceNameRef(reference.name), + mSymbols->push_back(std::make_pair(ResourceNameRef(reference.name), mOut->size() - sizeof(outMapEntry->value.data))); }); } @@ -188,16 +187,47 @@ public: private: BigBuffer* mOut; - std::vector<std::pair<ResourceNameRef, uint32_t>>& mSymbols; + SymbolEntryVector* mSymbols; android::ResTable_map_entry* mMap; }; +/** + * Flattens a value, with special handling for References. + */ +struct ValueFlattener : ConstValueVisitor { + ValueFlattener(BigBuffer* out, SymbolEntryVector* symbols) : + result(false), mOut(out), mOutValue(nullptr), mSymbols(symbols) { + mOutValue = mOut->nextBlock<android::Res_value>(); + } + + virtual void visit(const Reference& ref, ValueVisitorArgs& a) override { + visitItem(ref, a); + if (mOutValue->data == 0x0) { + mSymbols->push_back({ + ResourceNameRef(ref.name), + mOut->size() - sizeof(mOutValue->data)}); + } + } + + virtual void visitItem(const Item& item, ValueVisitorArgs&) override { + result = item.flatten(*mOutValue); + mOutValue->size = sizeof(*mOutValue); + } + + bool result; + +private: + BigBuffer* mOut; + android::Res_value* mOutValue; + SymbolEntryVector* mSymbols; +}; + TableFlattener::TableFlattener(Options options) : mOptions(options) { } bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry, - std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries) { + SymbolEntryVector* symbols) { if (flatEntry.value.isItem()) { android::ResTable_entry* entry = out->nextBlock<android::ResTable_entry>(); @@ -218,30 +248,16 @@ bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry, ResTable_entry_source* sourceBlock = out->nextBlock<ResTable_entry_source>(); sourceBlock->pathIndex = flatEntry.sourcePathKey; sourceBlock->line = flatEntry.sourceLine; - entry->size += sizeof(*sourceBlock); } - android::Res_value* outValue = out->nextBlock<android::Res_value>(); - - const Item& item = static_cast<const Item&>(flatEntry.value); - if (!item.flatten(*outValue)) { - return false; - } - - if (outValue->data == 0x0) { - visitFunc<Reference>(item, [&](const Reference& reference) { - symbolEntries.push_back({ - ResourceNameRef(reference.name), - out->size() - sizeof(outValue->data) - }); - }); - } - outValue->size = sizeof(*outValue); - return true; + const Item* item = static_cast<const Item*>(&flatEntry.value); + ValueFlattener flattener(out, symbols); + item->accept(flattener, {}); + return flattener.result; } - MapFlattener flattener(out, flatEntry, symbolEntries); + MapFlattener flattener(out, flatEntry, symbols); flatEntry.value.accept(flattener, {}); return true; } @@ -263,7 +279,7 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) { return false; } - std::vector<std::pair<ResourceNameRef, uint32_t>> symbolEntries; + SymbolEntryVector symbolEntries; StringPool typePool; StringPool keyPool; @@ -401,7 +417,7 @@ bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) { for (const FlatEntry& flatEntry : entry.second) { assert(flatEntry.entry.entryId < type->entries.size()); indices[flatEntry.entry.entryId] = typeBlock.size() - entryStart; - if (!flattenValue(&typeBlock, flatEntry, symbolEntries)) { + if (!flattenValue(&typeBlock, flatEntry, &symbolEntries)) { Logger::error() << "failed to flatten resource '" << ResourceNameRef { diff --git a/tools/aapt2/TableFlattener.h b/tools/aapt2/TableFlattener.h index 0ae798c..ccbb737 100644 --- a/tools/aapt2/TableFlattener.h +++ b/tools/aapt2/TableFlattener.h @@ -22,6 +22,8 @@ namespace aapt { +using SymbolEntryVector = std::vector<std::pair<ResourceNameRef, uint32_t>>; + struct FlatEntry; /** @@ -49,8 +51,7 @@ struct TableFlattener { bool flatten(BigBuffer* out, const ResourceTable& table); private: - bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, - std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries); + bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry, SymbolEntryVector* symbols); Options mOptions; }; diff --git a/tools/aapt2/ZipEntry.cpp b/tools/aapt2/ZipEntry.cpp index ad5d84a..891b4e1 100644 --- a/tools/aapt2/ZipEntry.cpp +++ b/tools/aapt2/ZipEntry.cpp @@ -144,9 +144,15 @@ void ZipEntry::initNew(const char* fileName, const char* comment) * Initializes the CDE and the LFH. */ status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */, - const ZipEntry* pEntry) + const ZipEntry* pEntry, const char* storageName) { mCDE = pEntry->mCDE; + if (storageName && *storageName != 0) { + mCDE.mFileNameLength = strlen(storageName); + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength + 1]; + strcpy((char*) mCDE.mFileName, storageName); + } + // Check whether we got all the memory needed. if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) || (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) || diff --git a/tools/aapt2/ZipEntry.h b/tools/aapt2/ZipEntry.h index d048a3e..2745a43 100644 --- a/tools/aapt2/ZipEntry.h +++ b/tools/aapt2/ZipEntry.h @@ -171,9 +171,10 @@ protected: /* * Initialize the structure with the contents of a ZipEntry from - * another file. + * another file. If fileName is non-NULL, override the name with fileName. */ - status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry); + status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry, + const char* fileName); /* * Add some pad bytes to the LFH. We do this by adding or resizing diff --git a/tools/aapt2/ZipFile.cpp b/tools/aapt2/ZipFile.cpp index 41e59cf..268c15e 100644 --- a/tools/aapt2/ZipFile.cpp +++ b/tools/aapt2/ZipFile.cpp @@ -546,7 +546,7 @@ bail: * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. */ status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, - int padding, ZipEntry** ppEntry) + const char* storageName, int padding, ZipEntry** ppEntry) { ZipEntry* pEntry = NULL; status_t result; @@ -570,9 +570,10 @@ status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, goto bail; } - result = pEntry->initFromExternal(pSourceZip, pSourceEntry); - if (result != NO_ERROR) + result = pEntry->initFromExternal(pSourceZip, pSourceEntry, storageName); + if (result != NO_ERROR) { goto bail; + } if (padding != 0) { result = pEntry->addPadding(padding); if (result != NO_ERROR) diff --git a/tools/aapt2/ZipFile.h b/tools/aapt2/ZipFile.h index 9cbd1fa..9de92dd 100644 --- a/tools/aapt2/ZipFile.h +++ b/tools/aapt2/ZipFile.h @@ -123,14 +123,16 @@ public: int compressionMethod, ZipEntry** ppEntry); /* - * Add an entry by copying it from another zip file. If "padding" is + * Add an entry by copying it from another zip file. If storageName is + * non-NULL, the entry will be inserted with the name storageName, otherwise + * it will have the same name as the source entry. If "padding" is * nonzero, the specified number of bytes will be added to the "extra" * field in the header. * * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. */ status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, - int padding, ZipEntry** ppEntry); + const char* storageName, int padding, ZipEntry** ppEntry); /* * Mark an entry as having been removed. It is not actually deleted diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile index 5a2a1d1..6b5fafa 100644 --- a/tools/aapt2/data/Makefile +++ b/tools/aapt2/data/Makefile @@ -2,10 +2,8 @@ # Environment dependent variables ## -SHELL := /bin/bash AAPT := aapt2 -ZIP := zip -n .arsc:.png:AndroidManifest.xml -ZIPALIGN := zipalign 4 +ZIPALIGN := zipalign -f 4 FRAMEWORK := ../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk ## diff --git a/tools/aapt2/data/lib/Makefile b/tools/aapt2/data/lib/Makefile index 2897ff1..8f56c54 100644 --- a/tools/aapt2/data/lib/Makefile +++ b/tools/aapt2/data/lib/Makefile @@ -3,7 +3,7 @@ ## AAPT := aapt2 -ZIPALIGN := zipalign 4 +ZIPALIGN := zipalign -f 4 FRAMEWORK := ../../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk ## diff --git a/tools/aapt2/data/lib/res/layout/main.xml b/tools/aapt2/data/lib/res/layout/main.xml new file mode 100644 index 0000000..187ed2d --- /dev/null +++ b/tools/aapt2/data/lib/res/layout/main.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"/> diff --git a/tools/aapt2/data/lib/res/raw/hello.txt b/tools/aapt2/data/lib/res/raw/hello.txt new file mode 100644 index 0000000..44fc22b --- /dev/null +++ b/tools/aapt2/data/lib/res/raw/hello.txt @@ -0,0 +1 @@ +Oh howdy there diff --git a/tools/obbtool/pbkdf2gen.cpp b/tools/obbtool/pbkdf2gen.cpp index 98d67c0..f1d8d04 100644 --- a/tools/obbtool/pbkdf2gen.cpp +++ b/tools/obbtool/pbkdf2gen.cpp @@ -20,6 +20,7 @@ #include <errno.h> #include <fcntl.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> |