Merge "Add AudioManager property strings for ultrasound."
diff --git a/api/current.txt b/api/current.txt
index feb2c2c..72b4bfb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -505,7 +505,6 @@
field public static final int dropDownWidth = 16843362; // 0x1010262
field public static final int duplicateParentState = 16842985; // 0x10100e9
field public static final int duration = 16843160; // 0x1010198
- field public static final int durationScaleHint = 16844014; // 0x10104ee
field public static final int dynamicResources = 16844019; // 0x10104f3
field public static final int editTextBackground = 16843602; // 0x1010352
field public static final int editTextColor = 16843601; // 0x1010351
@@ -996,6 +995,7 @@
field public static final int readPermission = 16842759; // 0x1010007
field public static final int recognitionService = 16843932; // 0x101049c
field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
+ field public static final int removeBeforeMRelease = 16844014; // 0x10104ee
field public static final int reparent = 16843964; // 0x10104bc
field public static final int reparentWithOverlay = 16843965; // 0x10104bd
field public static final int repeatCount = 16843199; // 0x10101bf
@@ -2868,7 +2868,6 @@
method public void cancel();
method public android.animation.Animator clone();
method public void end();
- method public long getDistanceBasedDuration();
method public abstract long getDuration();
method public android.animation.TimeInterpolator getInterpolator();
method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
@@ -2882,16 +2881,12 @@
method public void removePauseListener(android.animation.Animator.AnimatorPauseListener);
method public void resume();
method public abstract android.animation.Animator setDuration(long);
- method public void setDurationScaleHint(int, android.content.res.Resources);
method public abstract void setInterpolator(android.animation.TimeInterpolator);
method public abstract void setStartDelay(long);
method public void setTarget(java.lang.Object);
method public void setupEndValues();
method public void setupStartValues();
method public void start();
- field public static final int HINT_DISTANCE_DEFINED_IN_DP = 2; // 0x2
- field public static final int HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE = 1; // 0x1
- field public static final int HINT_NO_SCALE = 0; // 0x0
}
public static abstract interface Animator.AnimatorListener {
@@ -5011,6 +5006,7 @@
field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+ field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
@@ -6131,6 +6127,7 @@
}
public final class UsageStatsManager {
+ method public boolean isAppIdle(java.lang.String);
method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
@@ -11336,6 +11333,8 @@
method public static int getBitsPerPixel(int);
field public static final int DEPTH16 = 1144402265; // 0x44363159
field public static final int DEPTH_POINT_CLOUD = 257; // 0x101
+ field public static final int FLEX_RGBA_8888 = 42; // 0x2a
+ field public static final int FLEX_RGB_888 = 41; // 0x29
field public static final int JPEG = 256; // 0x100
field public static final int NV16 = 16; // 0x10
field public static final int NV21 = 17; // 0x11
@@ -11346,6 +11345,8 @@
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
field public static final int YUV_420_888 = 35; // 0x23
+ field public static final int YUV_422_888 = 39; // 0x27
+ field public static final int YUV_444_888 = 40; // 0x28
field public static final int YUY2 = 20; // 0x14
field public static final int YV12 = 842094169; // 0x32315659
}
@@ -14926,16 +14927,20 @@
public class AudioRecord {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
method public int getChannelConfiguration();
method public int getChannelCount();
+ method public android.media.AudioFormat getFormat();
method public static int getMinBufferSize(int, int, int);
method public int getNativeFrameCount() throws java.lang.IllegalStateException;
method public int getNotificationMarkerPosition();
method public int getPositionNotificationPeriod();
+ method public android.media.AudioDeviceInfo getPreferredInputDevice();
method public int getRecordingState();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int read(byte[], int, int);
@@ -14946,8 +14951,10 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
+ method public boolean setPreferredInputDevice(android.media.AudioDeviceInfo);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener, android.os.Handler);
method public void startRecording() throws java.lang.IllegalStateException;
@@ -14988,12 +14995,14 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getChannelConfiguration();
method public int getChannelCount();
+ method public android.media.AudioFormat getFormat();
method public static float getMaxVolume();
method public static int getMinBufferSize(int, int, int);
method public static float getMinVolume();
@@ -15006,6 +15015,7 @@
method public android.media.PlaybackSettings getPlaybackSettings();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredOutputDevice();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int getStreamType();
@@ -15014,6 +15024,7 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -15417,22 +15428,24 @@
field public static final deprecated int COLOR_Format24BitABGR6666 = 43; // 0x2b
field public static final deprecated int COLOR_Format24BitARGB6666 = 42; // 0x2a
field public static final deprecated int COLOR_Format24bitARGB1887 = 13; // 0xd
- field public static final deprecated int COLOR_Format24bitBGR888 = 12; // 0xc
- field public static final int COLOR_Format24bitRGB888 = 11; // 0xb
+ field public static final int COLOR_Format24bitBGR888 = 12; // 0xc
+ field public static final deprecated int COLOR_Format24bitRGB888 = 11; // 0xb
field public static final deprecated int COLOR_Format25bitARGB1888 = 14; // 0xe
- field public static final deprecated int COLOR_Format32BitRGBA8888 = 2130747392; // 0x7f00a000
- field public static final int COLOR_Format32bitARGB8888 = 16; // 0x10
- field public static final int COLOR_Format32bitBGRA8888 = 15; // 0xf
+ field public static final int COLOR_Format32bitABGR8888 = 2130747392; // 0x7f00a000
+ field public static final deprecated int COLOR_Format32bitARGB8888 = 16; // 0x10
+ field public static final deprecated int COLOR_Format32bitBGRA8888 = 15; // 0xf
field public static final deprecated int COLOR_Format8bitRGB332 = 2; // 0x2
field public static final deprecated int COLOR_FormatCbYCrY = 27; // 0x1b
field public static final deprecated int COLOR_FormatCrYCbY = 28; // 0x1c
field public static final int COLOR_FormatL16 = 36; // 0x24
field public static final deprecated int COLOR_FormatL2 = 33; // 0x21
field public static final deprecated int COLOR_FormatL24 = 37; // 0x25
- field public static final int COLOR_FormatL32 = 38; // 0x26
+ field public static final deprecated int COLOR_FormatL32 = 38; // 0x26
field public static final deprecated int COLOR_FormatL4 = 34; // 0x22
field public static final int COLOR_FormatL8 = 35; // 0x23
field public static final deprecated int COLOR_FormatMonochrome = 1; // 0x1
+ field public static final int COLOR_FormatRGBAFlexible = 2134288520; // 0x7f36a888
+ field public static final int COLOR_FormatRGBFlexible = 2134292616; // 0x7f36b888
field public static final int COLOR_FormatRawBayer10bit = 31; // 0x1f
field public static final int COLOR_FormatRawBayer8bit = 30; // 0x1e
field public static final int COLOR_FormatRawBayer8bitcompressed = 32; // 0x20
@@ -15451,7 +15464,8 @@
field public static final deprecated int COLOR_FormatYUV422PackedSemiPlanar = 40; // 0x28
field public static final deprecated int COLOR_FormatYUV422Planar = 22; // 0x16
field public static final deprecated int COLOR_FormatYUV422SemiPlanar = 24; // 0x18
- field public static final int COLOR_FormatYUV444Interleaved = 29; // 0x1d
+ field public static final int COLOR_FormatYUV444Flexible = 2135181448; // 0x7f444888
+ field public static final deprecated int COLOR_FormatYUV444Interleaved = 29; // 0x1d
field public static final deprecated int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00
field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
@@ -15819,6 +15833,7 @@
field public static final java.lang.String KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle";
field public static final java.lang.String KEY_I_FRAME_INTERVAL = "i-frame-interval";
field public static final java.lang.String KEY_LANGUAGE = "language";
+ field public static final java.lang.String KEY_LEVEL = "level";
field public static final java.lang.String KEY_MAX_HEIGHT = "max-height";
field public static final java.lang.String KEY_MAX_INPUT_SIZE = "max-input-size";
field public static final java.lang.String KEY_MAX_WIDTH = "max-width";
@@ -15828,7 +15843,10 @@
field public static final java.lang.String KEY_PROFILE = "profile";
field public static final java.lang.String KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
field public static final java.lang.String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
+ field public static final java.lang.String KEY_ROTATION = "rotation-degrees";
field public static final java.lang.String KEY_SAMPLE_RATE = "sample-rate";
+ field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height";
+ field public static final java.lang.String KEY_STRIDE = "stride";
field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
@@ -16372,10 +16390,13 @@
method public final void release();
method public void setAudioTrack(android.media.AudioTrack);
method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
+ method public void setOnErrorListener(android.media.MediaSync.OnErrorListener, android.os.Handler);
method public void setPlaybackRate(float, int);
method public void setPlaybackSettings(android.media.PlaybackSettings);
method public void setSurface(android.view.Surface);
method public void setSyncSettings(android.media.SyncSettings);
+ field public static final int MEDIASYNC_ERROR_AUDIOTRACK_FAIL = 1; // 0x1
+ field public static final int MEDIASYNC_ERROR_SURFACE_FAIL = 2; // 0x2
field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0
field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2
field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1
@@ -16383,7 +16404,11 @@
public static abstract class MediaSync.Callback {
ctor public MediaSync.Callback();
- method public abstract void onReturnAudioBuffer(android.media.MediaSync, java.nio.ByteBuffer, int);
+ method public abstract void onAudioBufferConsumed(android.media.MediaSync, java.nio.ByteBuffer, int);
+ }
+
+ public static abstract interface MediaSync.OnErrorListener {
+ method public abstract void onError(android.media.MediaSync, int, int);
}
public class MediaSyncEvent {
@@ -16409,6 +16434,14 @@
method public abstract void onAudioDeviceConnection();
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class PlaybackSettings {
ctor public PlaybackSettings();
method public android.media.PlaybackSettings allowDefaults();
@@ -23372,6 +23405,7 @@
method public final android.os.IBinder readStrongBinder();
method public final void readTypedArray(T[], android.os.Parcelable.Creator<T>);
method public final void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
+ method public final T readTypedObject(android.os.Parcelable.Creator<T>);
method public final java.lang.Object readValue(java.lang.ClassLoader);
method public final void recycle();
method public final void setDataCapacity(int);
@@ -23416,6 +23450,7 @@
method public final void writeStrongInterface(android.os.IInterface);
method public final void writeTypedArray(T[], int);
method public final void writeTypedList(java.util.List<T>);
+ method public final void writeTypedObject(T, int);
method public final void writeValue(java.lang.Object);
field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
}
@@ -28412,13 +28447,6 @@
package android.security {
- public class CryptoOperationException extends java.lang.RuntimeException {
- ctor public CryptoOperationException();
- ctor public CryptoOperationException(java.lang.String);
- ctor public CryptoOperationException(java.lang.String, java.lang.Throwable);
- ctor public CryptoOperationException(java.lang.Throwable);
- }
-
public class EcIesParameterSpec implements java.security.spec.AlgorithmParameterSpec {
method public int getDemCipherKeySize();
method public java.lang.String getDemCipherTransformation();
@@ -28475,7 +28503,7 @@
ctor public KeyChainException(java.lang.Throwable);
}
- public class KeyExpiredException extends android.security.CryptoOperationException {
+ public class KeyExpiredException extends java.security.InvalidKeyException {
ctor public KeyExpiredException();
ctor public KeyExpiredException(java.lang.String);
ctor public KeyExpiredException(java.lang.String, java.lang.Throwable);
@@ -28517,7 +28545,7 @@
method public android.security.KeyGeneratorSpec.Builder setUserAuthenticators(int);
}
- public class KeyNotYetValidException extends android.security.CryptoOperationException {
+ public class KeyNotYetValidException extends java.security.InvalidKeyException {
ctor public KeyNotYetValidException();
ctor public KeyNotYetValidException(java.lang.String);
ctor public KeyNotYetValidException(java.lang.String, java.lang.Throwable);
@@ -28665,12 +28693,12 @@
method public boolean isCleartextTrafficPermitted();
}
- public class NewFingerprintEnrolledException extends android.security.CryptoOperationException {
+ public class NewFingerprintEnrolledException extends java.security.InvalidKeyException {
ctor public NewFingerprintEnrolledException();
ctor public NewFingerprintEnrolledException(java.lang.String);
}
- public class UserNotAuthenticatedException extends android.security.CryptoOperationException {
+ public class UserNotAuthenticatedException extends java.security.InvalidKeyException {
ctor public UserNotAuthenticatedException();
ctor public UserNotAuthenticatedException(java.lang.String);
ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
@@ -30104,8 +30132,8 @@
field public static final int STATE_DISCONNECTING = 10; // 0xa
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
- field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
field public static final int STATE_RINGING = 2; // 0x2
+ field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
public static abstract class Call.Callback {
@@ -30165,20 +30193,6 @@
field public static final int CONFERENCE = 1; // 0x1
}
- public final class CallState {
- method public static java.lang.String toString(int);
- field public static final int ABORTED = 8; // 0x8
- field public static final int ACTIVE = 5; // 0x5
- field public static final int CONNECTING = 1; // 0x1
- field public static final int DIALING = 3; // 0x3
- field public static final int DISCONNECTED = 7; // 0x7
- field public static final int DISCONNECTING = 9; // 0x9
- field public static final int NEW = 0; // 0x0
- field public static final int ON_HOLD = 6; // 0x6
- field public static final int PRE_DIAL_WAIT = 2; // 0x2
- field public static final int RINGING = 4; // 0x4
- }
-
public final class CameraCapabilities implements android.os.Parcelable {
ctor public CameraCapabilities(int, int);
method public int describeContents();
@@ -30586,6 +30600,7 @@
method public void cancelMissedCallsNotification();
method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
+ method public java.lang.String getDefaultDialerPackage();
method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
method public java.lang.String getLine1Number(android.telecom.PhoneAccountHandle);
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -30595,10 +30610,12 @@
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+ method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
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";
@@ -30609,6 +30626,7 @@
field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+ field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
@@ -30659,6 +30677,7 @@
method public android.os.Bundle getConfigForSubId(int);
method public void reloadCarrierConfigForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final java.lang.String BOOL_APN_EXPAND = "bool_apn_expand";
field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
diff --git a/api/system-current.txt b/api/system-current.txt
index 5084f55..b599091 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -578,7 +578,6 @@
field public static final int dropDownWidth = 16843362; // 0x1010262
field public static final int duplicateParentState = 16842985; // 0x10100e9
field public static final int duration = 16843160; // 0x1010198
- field public static final int durationScaleHint = 16844014; // 0x10104ee
field public static final int dynamicResources = 16844019; // 0x10104f3
field public static final int editTextBackground = 16843602; // 0x1010352
field public static final int editTextColor = 16843601; // 0x1010351
@@ -1069,6 +1068,7 @@
field public static final int readPermission = 16842759; // 0x1010007
field public static final int recognitionService = 16843932; // 0x101049c
field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
+ field public static final int removeBeforeMRelease = 16844014; // 0x10104ee
field public static final int reparent = 16843964; // 0x10104bc
field public static final int reparentWithOverlay = 16843965; // 0x10104bd
field public static final int repeatCount = 16843199; // 0x10101bf
@@ -2948,7 +2948,6 @@
method public void cancel();
method public android.animation.Animator clone();
method public void end();
- method public long getDistanceBasedDuration();
method public abstract long getDuration();
method public android.animation.TimeInterpolator getInterpolator();
method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
@@ -2962,16 +2961,12 @@
method public void removePauseListener(android.animation.Animator.AnimatorPauseListener);
method public void resume();
method public abstract android.animation.Animator setDuration(long);
- method public void setDurationScaleHint(int, android.content.res.Resources);
method public abstract void setInterpolator(android.animation.TimeInterpolator);
method public abstract void setStartDelay(long);
method public void setTarget(java.lang.Object);
method public void setupEndValues();
method public void setupStartValues();
method public void start();
- field public static final int HINT_DISTANCE_DEFINED_IN_DP = 2; // 0x2
- field public static final int HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE = 1; // 0x1
- field public static final int HINT_NO_SCALE = 0; // 0x0
}
public static abstract interface Animator.AnimatorListener {
@@ -5102,6 +5097,7 @@
field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+ field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
@@ -6319,6 +6315,7 @@
}
public final class UsageStatsManager {
+ method public boolean isAppIdle(java.lang.String);
method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
@@ -11631,6 +11628,8 @@
method public static int getBitsPerPixel(int);
field public static final int DEPTH16 = 1144402265; // 0x44363159
field public static final int DEPTH_POINT_CLOUD = 257; // 0x101
+ field public static final int FLEX_RGBA_8888 = 42; // 0x2a
+ field public static final int FLEX_RGB_888 = 41; // 0x29
field public static final int JPEG = 256; // 0x100
field public static final int NV16 = 16; // 0x10
field public static final int NV21 = 17; // 0x11
@@ -11641,6 +11640,8 @@
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
field public static final int YUV_420_888 = 35; // 0x23
+ field public static final int YUV_422_888 = 39; // 0x27
+ field public static final int YUV_444_888 = 40; // 0x28
field public static final int YUY2 = 20; // 0x14
field public static final int YV12 = 842094169; // 0x32315659
}
@@ -16137,16 +16138,20 @@
public class AudioRecord {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener, android.os.Handler);
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getAudioSource();
method public int getChannelConfiguration();
method public int getChannelCount();
+ method public android.media.AudioFormat getFormat();
method public static int getMinBufferSize(int, int, int);
method public int getNativeFrameCount() throws java.lang.IllegalStateException;
method public int getNotificationMarkerPosition();
method public int getPositionNotificationPeriod();
+ method public android.media.AudioDeviceInfo getPreferredInputDevice();
method public int getRecordingState();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int read(byte[], int, int);
@@ -16157,8 +16162,10 @@
method public int read(java.nio.ByteBuffer, int);
method public int read(java.nio.ByteBuffer, int, int);
method public void release();
+ method public void removeOnAudioRecordRoutingListener(android.media.OnAudioRecordRoutingListener);
method public int setNotificationMarkerPosition(int);
method public int setPositionNotificationPeriod(int);
+ method public boolean setPreferredInputDevice(android.media.AudioDeviceInfo);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener);
method public void setRecordPositionUpdateListener(android.media.AudioRecord.OnRecordPositionUpdateListener, android.os.Handler);
method public void startRecording() throws java.lang.IllegalStateException;
@@ -16201,12 +16208,14 @@
ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
+ method public void addOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener, android.os.Handler);
method public int attachAuxEffect(int);
method public void flush();
method public int getAudioFormat();
method public int getAudioSessionId();
method public int getChannelConfiguration();
method public int getChannelCount();
+ method public android.media.AudioFormat getFormat();
method public static float getMaxVolume();
method public static int getMinBufferSize(int, int, int);
method public static float getMinVolume();
@@ -16219,6 +16228,7 @@
method public android.media.PlaybackSettings getPlaybackSettings();
method public int getPositionNotificationPeriod();
method public android.media.AudioDeviceInfo getPreferredOutputDevice();
+ method public android.media.AudioDeviceInfo getRoutedDevice();
method public int getSampleRate();
method public int getState();
method public int getStreamType();
@@ -16227,6 +16237,7 @@
method public void play() throws java.lang.IllegalStateException;
method public void release();
method public int reloadStaticData();
+ method public void removeOnAudioTrackRoutingListener(android.media.OnAudioTrackRoutingListener);
method public int setAuxEffectSendLevel(float);
method public int setLoopPoints(int, int, int);
method public int setNotificationMarkerPosition(int);
@@ -16630,22 +16641,24 @@
field public static final deprecated int COLOR_Format24BitABGR6666 = 43; // 0x2b
field public static final deprecated int COLOR_Format24BitARGB6666 = 42; // 0x2a
field public static final deprecated int COLOR_Format24bitARGB1887 = 13; // 0xd
- field public static final deprecated int COLOR_Format24bitBGR888 = 12; // 0xc
- field public static final int COLOR_Format24bitRGB888 = 11; // 0xb
+ field public static final int COLOR_Format24bitBGR888 = 12; // 0xc
+ field public static final deprecated int COLOR_Format24bitRGB888 = 11; // 0xb
field public static final deprecated int COLOR_Format25bitARGB1888 = 14; // 0xe
- field public static final deprecated int COLOR_Format32BitRGBA8888 = 2130747392; // 0x7f00a000
- field public static final int COLOR_Format32bitARGB8888 = 16; // 0x10
- field public static final int COLOR_Format32bitBGRA8888 = 15; // 0xf
+ field public static final int COLOR_Format32bitABGR8888 = 2130747392; // 0x7f00a000
+ field public static final deprecated int COLOR_Format32bitARGB8888 = 16; // 0x10
+ field public static final deprecated int COLOR_Format32bitBGRA8888 = 15; // 0xf
field public static final deprecated int COLOR_Format8bitRGB332 = 2; // 0x2
field public static final deprecated int COLOR_FormatCbYCrY = 27; // 0x1b
field public static final deprecated int COLOR_FormatCrYCbY = 28; // 0x1c
field public static final int COLOR_FormatL16 = 36; // 0x24
field public static final deprecated int COLOR_FormatL2 = 33; // 0x21
field public static final deprecated int COLOR_FormatL24 = 37; // 0x25
- field public static final int COLOR_FormatL32 = 38; // 0x26
+ field public static final deprecated int COLOR_FormatL32 = 38; // 0x26
field public static final deprecated int COLOR_FormatL4 = 34; // 0x22
field public static final int COLOR_FormatL8 = 35; // 0x23
field public static final deprecated int COLOR_FormatMonochrome = 1; // 0x1
+ field public static final int COLOR_FormatRGBAFlexible = 2134288520; // 0x7f36a888
+ field public static final int COLOR_FormatRGBFlexible = 2134292616; // 0x7f36b888
field public static final int COLOR_FormatRawBayer10bit = 31; // 0x1f
field public static final int COLOR_FormatRawBayer8bit = 30; // 0x1e
field public static final int COLOR_FormatRawBayer8bitcompressed = 32; // 0x20
@@ -16664,7 +16677,8 @@
field public static final deprecated int COLOR_FormatYUV422PackedSemiPlanar = 40; // 0x28
field public static final deprecated int COLOR_FormatYUV422Planar = 22; // 0x16
field public static final deprecated int COLOR_FormatYUV422SemiPlanar = 24; // 0x18
- field public static final int COLOR_FormatYUV444Interleaved = 29; // 0x1d
+ field public static final int COLOR_FormatYUV444Flexible = 2135181448; // 0x7f444888
+ field public static final deprecated int COLOR_FormatYUV444Interleaved = 29; // 0x1d
field public static final deprecated int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00
field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
@@ -17033,6 +17047,7 @@
field public static final java.lang.String KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle";
field public static final java.lang.String KEY_I_FRAME_INTERVAL = "i-frame-interval";
field public static final java.lang.String KEY_LANGUAGE = "language";
+ field public static final java.lang.String KEY_LEVEL = "level";
field public static final java.lang.String KEY_MAX_HEIGHT = "max-height";
field public static final java.lang.String KEY_MAX_INPUT_SIZE = "max-input-size";
field public static final java.lang.String KEY_MAX_WIDTH = "max-width";
@@ -17042,7 +17057,10 @@
field public static final java.lang.String KEY_PROFILE = "profile";
field public static final java.lang.String KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
field public static final java.lang.String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
+ field public static final java.lang.String KEY_ROTATION = "rotation-degrees";
field public static final java.lang.String KEY_SAMPLE_RATE = "sample-rate";
+ field public static final java.lang.String KEY_SLICE_HEIGHT = "slice-height";
+ field public static final java.lang.String KEY_STRIDE = "stride";
field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
@@ -17588,10 +17606,13 @@
method public final void release();
method public void setAudioTrack(android.media.AudioTrack);
method public void setCallback(android.media.MediaSync.Callback, android.os.Handler);
+ method public void setOnErrorListener(android.media.MediaSync.OnErrorListener, android.os.Handler);
method public void setPlaybackRate(float, int);
method public void setPlaybackSettings(android.media.PlaybackSettings);
method public void setSurface(android.view.Surface);
method public void setSyncSettings(android.media.SyncSettings);
+ field public static final int MEDIASYNC_ERROR_AUDIOTRACK_FAIL = 1; // 0x1
+ field public static final int MEDIASYNC_ERROR_SURFACE_FAIL = 2; // 0x2
field public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; // 0x0
field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; // 0x2
field public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; // 0x1
@@ -17599,7 +17620,11 @@
public static abstract class MediaSync.Callback {
ctor public MediaSync.Callback();
- method public abstract void onReturnAudioBuffer(android.media.MediaSync, java.nio.ByteBuffer, int);
+ method public abstract void onAudioBufferConsumed(android.media.MediaSync, java.nio.ByteBuffer, int);
+ }
+
+ public static abstract interface MediaSync.OnErrorListener {
+ method public abstract void onError(android.media.MediaSync, int, int);
}
public class MediaSyncEvent {
@@ -17625,6 +17650,14 @@
method public abstract void onAudioDeviceConnection();
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class PlaybackSettings {
ctor public PlaybackSettings();
method public android.media.PlaybackSettings allowDefaults();
@@ -25261,6 +25294,7 @@
method public final android.os.IBinder readStrongBinder();
method public final void readTypedArray(T[], android.os.Parcelable.Creator<T>);
method public final void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
+ method public final T readTypedObject(android.os.Parcelable.Creator<T>);
method public final java.lang.Object readValue(java.lang.ClassLoader);
method public final void recycle();
method public final void setDataCapacity(int);
@@ -25305,6 +25339,7 @@
method public final void writeStrongInterface(android.os.IInterface);
method public final void writeTypedArray(T[], int);
method public final void writeTypedList(java.util.List<T>);
+ method public final void writeTypedObject(T, int);
method public final void writeValue(java.lang.Object);
field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
}
@@ -30416,13 +30451,6 @@
package android.security {
- public class CryptoOperationException extends java.lang.RuntimeException {
- ctor public CryptoOperationException();
- ctor public CryptoOperationException(java.lang.String);
- ctor public CryptoOperationException(java.lang.String, java.lang.Throwable);
- ctor public CryptoOperationException(java.lang.Throwable);
- }
-
public class EcIesParameterSpec implements java.security.spec.AlgorithmParameterSpec {
method public int getDemCipherKeySize();
method public java.lang.String getDemCipherTransformation();
@@ -30479,7 +30507,7 @@
ctor public KeyChainException(java.lang.Throwable);
}
- public class KeyExpiredException extends android.security.CryptoOperationException {
+ public class KeyExpiredException extends java.security.InvalidKeyException {
ctor public KeyExpiredException();
ctor public KeyExpiredException(java.lang.String);
ctor public KeyExpiredException(java.lang.String, java.lang.Throwable);
@@ -30521,7 +30549,7 @@
method public android.security.KeyGeneratorSpec.Builder setUserAuthenticators(int);
}
- public class KeyNotYetValidException extends android.security.CryptoOperationException {
+ public class KeyNotYetValidException extends java.security.InvalidKeyException {
ctor public KeyNotYetValidException();
ctor public KeyNotYetValidException(java.lang.String);
ctor public KeyNotYetValidException(java.lang.String, java.lang.Throwable);
@@ -30669,12 +30697,12 @@
method public boolean isCleartextTrafficPermitted();
}
- public class NewFingerprintEnrolledException extends android.security.CryptoOperationException {
+ public class NewFingerprintEnrolledException extends java.security.InvalidKeyException {
ctor public NewFingerprintEnrolledException();
ctor public NewFingerprintEnrolledException(java.lang.String);
}
- public class UserNotAuthenticatedException extends android.security.CryptoOperationException {
+ public class UserNotAuthenticatedException extends java.security.InvalidKeyException {
ctor public UserNotAuthenticatedException();
ctor public UserNotAuthenticatedException(java.lang.String);
ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
@@ -32210,8 +32238,9 @@
field public static final int STATE_DISCONNECTING = 10; // 0xa
field public static final int STATE_HOLDING = 3; // 0x3
field public static final int STATE_NEW = 0; // 0x0
- field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
+ field public static final deprecated int STATE_PRE_DIAL_WAIT = 8; // 0x8
field public static final int STATE_RINGING = 2; // 0x2
+ field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
}
public static abstract class Call.Callback {
@@ -32275,20 +32304,6 @@
field public static final int CONFERENCE = 1; // 0x1
}
- public final class CallState {
- method public static java.lang.String toString(int);
- field public static final int ABORTED = 8; // 0x8
- field public static final int ACTIVE = 5; // 0x5
- field public static final int CONNECTING = 1; // 0x1
- field public static final int DIALING = 3; // 0x3
- field public static final int DISCONNECTED = 7; // 0x7
- field public static final int DISCONNECTING = 9; // 0x9
- field public static final int NEW = 0; // 0x0
- field public static final int ON_HOLD = 6; // 0x6
- field public static final int PRE_DIAL_WAIT = 2; // 0x2
- field public static final int RINGING = 4; // 0x4
- }
-
public final class CameraCapabilities implements android.os.Parcelable {
ctor public CameraCapabilities(int, int);
method public int describeContents();
@@ -32729,8 +32744,9 @@
method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
method public int getCallState();
method public android.telecom.PhoneAccountHandle getConnectionManager();
+ method public java.lang.String getDefaultDialerPackage();
method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
- method public android.content.ComponentName getDefaultPhoneApp();
+ method public deprecated android.content.ComponentName getDefaultPhoneApp();
method public java.lang.String getLine1Number(android.telecom.PhoneAccountHandle);
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
@@ -32744,10 +32760,12 @@
method public boolean isRinging();
method public boolean isTtySupported();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+ method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
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";
@@ -32760,6 +32778,7 @@
field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+ field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
@@ -32813,6 +32832,7 @@
method public void reloadCarrierConfigForSubId(int);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ field public static final java.lang.String BOOL_APN_EXPAND = "bool_apn_expand";
field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
@@ -41572,10 +41592,19 @@
ctor public WebViewFactory();
method public static android.content.pm.PackageInfo getLoadedPackageInfo();
method public static java.lang.String getWebViewPackageName();
+ method public static int loadWebViewNativeLibraryFromPackage(java.lang.String);
method public static void onWebViewUpdateInstalled();
method public static void prepareWebViewInSystemServer();
method public static void prepareWebViewInZygote();
field public static final java.lang.String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = "persist.sys.webview.vmsize";
+ field public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; // 0x2
+ field public static final int LIBLOAD_FAILED_JNI_CALL = 7; // 0x7
+ field public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; // 0x4
+ field public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6; // 0x6
+ field public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5; // 0x5
+ field public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3; // 0x3
+ field public static final int LIBLOAD_SUCCESS = 0; // 0x0
+ field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1
}
public abstract interface WebViewFactoryProvider {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 8ba2a5a..219d35b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -142,6 +142,8 @@
" am task resizeable <TASK_ID> [true|false]\n" +
" am task resize <TASK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
" am get-config\n" +
+ " am set-idle [--user <USER_ID>] <PACKAGE> true|false\n" +
+ " am get-idle [--user <USER_ID>] <PACKAGE>\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
@@ -282,6 +284,11 @@
"am get-config: retrieve the configuration and any recent configurations\n" +
" of the device\n" +
"\n" +
+ "am set-idle: sets the idle state of an app\n" +
+ "\n" +
+ "am get-idle: returns the idle state of an app\n" +
+ "\n" +
+ "\n" +
"<INTENT> specifications include these flags and arguments:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
" [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
@@ -388,6 +395,10 @@
runTask();
} else if (op.equals("get-config")) {
runGetConfig();
+ } else if (op.equals("set-idle")) {
+ runSetIdle();
+ } else if (op.equals("get-idle")) {
+ runGetIdle();
} else {
showError("Error: unknown command '" + op + "'");
}
@@ -2019,6 +2030,46 @@
}
}
+ private void runSetIdle() throws Exception {
+ int userId = UserHandle.USER_OWNER;
+
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = parseUserArg(nextArgRequired());
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ return;
+ }
+ }
+ String packageName = nextArgRequired();
+ String value = nextArgRequired();
+
+ IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+ Context.USAGE_STATS_SERVICE));
+ usm.setAppIdle(packageName, Boolean.parseBoolean(value), userId);
+ }
+
+ private void runGetIdle() throws Exception {
+ int userId = UserHandle.USER_OWNER;
+
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = parseUserArg(nextArgRequired());
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ return;
+ }
+ }
+ String packageName = nextArgRequired();
+
+ IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+ Context.USAGE_STATS_SERVICE));
+ boolean isIdle = usm.isAppIdle(packageName, userId);
+ System.out.println("Idle=" + isIdle);
+ }
+
/**
* Open the given file for sending into the system process. This verifies
* with SELinux that the system will have access to the file.
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d5cc8cc..b84b1e2 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -51,9 +51,11 @@
import android.os.IUserManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.Log;
import libcore.io.IoUtils;
@@ -1283,20 +1285,6 @@
}
}
- class LocalPackageMoveObserver extends IPackageMoveObserver.Stub {
- boolean finished;
- int returnCode;
-
- @Override
- public void packageMoved(String packageName, int returnCode) throws RemoteException {
- synchronized (this) {
- this.finished = true;
- this.returnCode = returnCode;
- notifyAll();
- }
- }
- }
-
public int runMove() {
final String packageName = nextArg();
String volumeUuid = nextArg();
@@ -1304,24 +1292,21 @@
volumeUuid = null;
}
- final LocalPackageMoveObserver obs = new LocalPackageMoveObserver();
try {
- mPm.movePackageAndData(packageName, volumeUuid, obs);
+ final int moveId = mPm.movePackage(packageName, volumeUuid);
- synchronized (obs) {
- while (!obs.finished) {
- try {
- obs.wait();
- } catch (InterruptedException e) {
- }
- }
- if (obs.returnCode == PackageManager.MOVE_SUCCEEDED) {
- System.out.println("Success");
- return 0;
- } else {
- System.err.println("Failure [" + obs.returnCode + "]");
- return 1;
- }
+ 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();
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 02a329d..da48709 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -16,12 +16,7 @@
package android.animation;
-import android.content.res.Configuration;
import android.content.res.ConstantState;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.animation.AnimationUtils;
import java.util.ArrayList;
@@ -30,29 +25,6 @@
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
public abstract class Animator implements Cloneable {
- /**
- * Set this hint when duration for the animation does not need to be scaled. By default, no
- * scaling is applied to the duration.
- */
- public static final int HINT_NO_SCALE = 0;
-
- /**
- * Set this scale hint (using {@link #setDurationScaleHint(int, Resources)} when the animation's
- * moving distance is proportional to the screen size. (e.g. a view coming in from the bottom of
- * the screen to top/center). With this scale hint set, the animation duration will be
- * automatically scaled based on screen size.
- */
- public static final int HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE = 1;
-
- /**
- * Set this scale hint (using {@link #setDurationScaleHint(int, Resources)}) if the animation
- * has pre-defined moving distance in dp that does not vary from device to device. This is
- * extremely useful when the animation needs to run on both phones/tablets and TV, because TV
- * has inflated dp and therefore will have a longer visual arc for the same animation than on
- * the phone. This hint is used to calculate a scaling factor to compensate for different
- * visual arcs while maintaining the same angular velocity for the animation.
- */
- public static final int HINT_DISTANCE_DEFINED_IN_DP = 2;
/**
* The set of listeners to be sent events through the life of an animation.
@@ -83,24 +55,6 @@
private AnimatorConstantState mConstantState;
/**
- * Scaling factor for an animation that moves across the whole screen.
- */
- float mScreenSizeBasedDurationScale = 1.0f;
-
- /**
- * Scaling factor for an animation that is defined to move the same amount of dp across all
- * devices.
- */
- float mDpBasedDurationScale = 1.0f;
-
- /**
- * By default, the scaling assumes the animation moves across the entire screen.
- */
- int mDurationScaleHint = HINT_NO_SCALE;
-
- private final static boolean ANIM_DEBUG = false;
-
- /**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
* value(s) set immediately, followed by calls to
@@ -230,78 +184,6 @@
public abstract long getDuration();
/**
- * Hints how duration scaling factor should be calculated. The duration will not be scaled when
- * hint is set to {@link #HINT_NO_SCALE}. Otherwise, the duration will be automatically scaled
- * per device to achieve the same look and feel across different devices. In order to do
- * that, the same angular velocity of the animation will be needed on different devices in
- * users' field of view. Therefore, the duration scale factor is determined by the ratio of the
- * angular movement on current devices to that on the baseline device (i.e. Nexus 5).
- *
- * @param hint an indicator on how the animation is defined. The hint could be
- * {@link #HINT_NO_SCALE}, {@link #HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE} or
- * {@link #HINT_DISTANCE_DEFINED_IN_DP}.
- * @param res The resources {@see android.content.res.Resources} for getting display metrics
- */
- public void setDurationScaleHint(int hint, Resources res) {
- if (ANIM_DEBUG) {
- Log.d("ANIM_DEBUG", "distance based duration hint: " + hint);
- }
- if (hint == mDurationScaleHint) {
- return;
- }
- mDurationScaleHint = hint;
- if (hint != HINT_NO_SCALE) {
- int uiMode = res.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK;
- DisplayMetrics metrics = res.getDisplayMetrics();
- float width = metrics.widthPixels / metrics.xdpi;
- float height = metrics.heightPixels / metrics.ydpi;
- float viewingDistance = AnimationUtils.getViewingDistance(width, height, uiMode);
- if (ANIM_DEBUG) {
- Log.d("ANIM_DEBUG", "width, height, viewing distance, uimode: "
- + width + ", " + height + ", " + viewingDistance + ", " + uiMode);
- }
- mScreenSizeBasedDurationScale = AnimationUtils
- .getScreenSizeBasedDurationScale(width, height, viewingDistance);
- mDpBasedDurationScale = AnimationUtils.getDpBasedDurationScale(
- metrics.density, metrics.xdpi, viewingDistance);
- if (ANIM_DEBUG) {
- Log.d("ANIM_DEBUG", "screen based scale, dp based scale: " +
- mScreenSizeBasedDurationScale + ", " + mDpBasedDurationScale);
- }
- }
- }
-
- // Copies duration scale hint and scaling factors to the new animation.
- void copyDurationScaleInfoTo(Animator anim) {
- anim.mDurationScaleHint = mDurationScaleHint;
- anim.mScreenSizeBasedDurationScale = mScreenSizeBasedDurationScale;
- anim.mDpBasedDurationScale = mDpBasedDurationScale;
- }
-
- /**
- * @return The scaled duration calculated based on distance of movement (as defined by the
- * animation) and perceived velocity (derived from the duration set on the animation for
- * baseline device)
- */
- public long getDistanceBasedDuration() {
- return (long) (getDuration() * getDistanceBasedDurationScale());
- }
-
- /**
- * @return scaling factor of duration based on the duration scale hint. A scaling factor of 1
- * means no scaling will be applied to the duration.
- */
- float getDistanceBasedDurationScale() {
- if (mDurationScaleHint == HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE) {
- return mScreenSizeBasedDurationScale;
- } else if (mDurationScaleHint == HINT_DISTANCE_DEFINED_IN_DP) {
- return mDpBasedDurationScale;
- } else {
- return 1f;
- }
- }
-
- /**
* The time interpolator used in calculating the elapsed fraction of the
* animation. The interpolator determines whether the animation runs with
* linear or non-linear motion, such as acceleration and deceleration. The
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index e47d017..427ecce 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -70,13 +70,6 @@
private static final int VALUE_TYPE_COLOR = 3;
private static final int VALUE_TYPE_UNDEFINED = 4;
- /**
- * Enum values used in XML attributes to indicate the duration scale hint.
- */
- private static final int HINT_NO_SCALE = 0;
- private static final int HINT_PROPORTIONAL_TO_SCREEN = 1;
- private static final int HINT_DEFINED_IN_DP = 2;
-
private static final boolean DBG_ANIMATOR_INFLATER = false;
// used to calculate changing configs for resource references
@@ -698,9 +691,6 @@
int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER);
createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering,
pixelSize);
- final int hint = a.getInt(R.styleable.AnimatorSet_durationScaleHint,
- HINT_NO_SCALE);
- anim.setDurationScaleHint(hint, res);
a.recycle();
} else if (name.equals("propertyValuesHolder")) {
PropertyValuesHolder[] values = loadValues(res, theme, parser,
@@ -1065,9 +1055,6 @@
anim.setInterpolator(interpolator);
}
- final int hint = arrayAnimator.getInt(R.styleable.Animator_durationScaleHint,
- HINT_NO_SCALE);
- anim.setDurationScaleHint(hint, res);
arrayAnimator.recycle();
if (arrayObjectAnimator != null) {
arrayObjectAnimator.recycle();
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index f6ad847..6503d89 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -519,7 +519,6 @@
for (Node node : mNodes) {
node.animation.setAllowRunningAsynchronously(false);
- copyDurationScaleInfoTo(node.animation);
}
if (mDuration >= 0) {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 292507b..a455f8b 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -17,12 +17,9 @@
package android.animation;
import android.annotation.CallSuper;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Choreographer;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -564,7 +561,7 @@
}
private void updateScaledDuration() {
- mDuration = (long)(mUnscaledDuration * sDurationScale * getDistanceBasedDurationScale());
+ mDuration = (long)(mUnscaledDuration * sDurationScale);
}
/**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ab5f811..9bad9bb 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -255,23 +255,18 @@
}
}
- static final class AcquiringProviderRecord {
- IActivityManager.ContentProviderHolder holder;
- boolean acquiring = true;
- int requests = 1;
- // Set if there was a runtime exception when trying to acquire the provider.
- RuntimeException runtimeException = null;
- }
-
// The lock of mProviderMap protects the following variables.
- final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<>();
- final ArrayMap<ProviderKey, AcquiringProviderRecord> mAcquiringProviderMap = new ArrayMap<>();
- final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap = new ArrayMap<>();
- final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders = new ArrayMap<>();
- final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName = new ArrayMap<>();
+ final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
+ = new ArrayMap<ProviderKey, ProviderClientRecord>();
+ final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
+ = new ArrayMap<IBinder, ProviderRefCount>();
+ final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
+ = new ArrayMap<IBinder, ProviderClientRecord>();
+ final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
+ = new ArrayMap<ComponentName, ProviderClientRecord>();
final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
- = new ArrayMap<>();
+ = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false;
@@ -351,7 +346,7 @@
}
}
- static final class ProviderClientRecord {
+ final class ProviderClientRecord {
final String[] mNames;
final IContentProvider mProvider;
final ContentProvider mLocalProvider;
@@ -4716,74 +4711,23 @@
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
- final ProviderKey key = new ProviderKey(auth, userId);
- final IContentProvider provider = acquireExistingProvider(c, key, stable);
+ final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
- AcquiringProviderRecord r;
- boolean first = false;
- synchronized (mAcquiringProviderMap) {
- r = mAcquiringProviderMap.get(key);
- if (r == null) {
- r = new AcquiringProviderRecord();
- mAcquiringProviderMap.put(key, r);
- first = true;
- } else {
- r.requests++;
- }
- }
+ // There is a possible race here. Another thread may try to acquire
+ // the same provider at the same time. When this happens, we want to ensure
+ // that the first one wins.
+ // Note that we cannot hold the lock while acquiring and installing the
+ // provider since it might take a long time to run and it could also potentially
+ // be re-entrant in the case where the provider is in the same process.
IActivityManager.ContentProviderHolder holder = null;
try {
- if (first) {
- // Multiple threads may try to acquire the same provider at the same time.
- // When this happens, we only let the first one really gets provider.
- // Other threads just wait for its result.
- // Note that we cannot hold the lock while acquiring and installing the
- // provider since it might take a long time to run and it could also potentially
- // be re-entrant in the case where the provider is in the same process.
- holder = ActivityManagerNative.getDefault().getContentProvider(
- getApplicationThread(), auth, userId, stable);
- } else {
- synchronized (r) {
- while (r.acquiring) {
- try {
- r.wait();
- } catch (InterruptedException e) {
- }
- }
- holder = r.holder;
- }
- }
+ holder = ActivityManagerNative.getDefault().getContentProvider(
+ getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
- } catch (RuntimeException e) {
- synchronized (r) {
- r.runtimeException = e;
- }
- } finally {
- if (first) {
- synchronized (r) {
- r.holder = holder;
- r.acquiring = false;
- r.notifyAll();
- }
- }
-
- synchronized (mAcquiringProviderMap) {
- if (--r.requests == 0) {
- mAcquiringProviderMap.remove(key);
- }
- }
-
- if (r.runtimeException != null) {
- // Was set when the first thread tried to acquire the provider,
- // but we should make sure it is thrown for all threads trying to
- // acquire the provider.
- throw r.runtimeException;
- }
}
-
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
@@ -4866,12 +4810,8 @@
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
- return acquireExistingProvider(c, new ProviderKey(auth, userId), stable);
- }
-
- final IContentProvider acquireExistingProvider(
- Context c, ProviderKey key, boolean stable) {
synchronized (mProviderMap) {
+ final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
@@ -4882,7 +4822,7 @@
if (!jBinder.isBinderAlive()) {
// The hosting process of the provider has died; we can't
// use this one.
- Log.i(TAG, "Acquiring provider " + key.authority + " for user " + key.userId
+ Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
+ ": existing object's process dead");
handleUnstableProviderDiedLocked(jBinder, true);
return null;
@@ -5204,12 +5144,18 @@
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count");
}
- // The provider has already been installed, so we need
- // to increase reference count to the existing one, but
- // only if release is needed (that is, it is not running
- // in the system process or local to the process).
+ // We need to transfer our new reference to the existing
+ // ref count, releasing the old one... but only if
+ // release is needed (that is, it is not running in the
+ // system process).
if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable);
+ try {
+ ActivityManagerNative.getDefault().removeContentProvider(
+ holder.connection, stable);
+ } catch (RemoteException e) {
+ //do nothing content provider object is dead any way
+ }
}
} else {
ProviderClientRecord client = installProviderAuthoritiesLocked(
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index dfe7e18..10f5960 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,6 +62,9 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -81,7 +84,9 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/*package*/
final class ApplicationPackageManager extends PackageManager {
@@ -98,6 +103,9 @@
@GuardedBy("mLock")
private PackageInstaller mInstaller;
+ @GuardedBy("mDelegates")
+ private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
+
UserManager getUserManager() {
synchronized (mLock) {
if (mUserManager == null) {
@@ -1410,57 +1418,100 @@
}
@Override
- public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ public String getInstallerPackageName(String packageName) {
try {
- mPM.movePackage(packageName, observer, flags);
+ return mPM.getInstallerPackageName(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return null;
+ }
+
+ @Override
+ public int getMoveStatus(int moveId) {
+ try {
+ return mPM.getMoveStatus(moveId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer) {
- try {
- mPM.movePackageAndData(packageName, volumeUuid, observer);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ public void registerMoveCallback(MoveCallback callback, Handler handler) {
+ synchronized (mDelegates) {
+ final MoveCallbackDelegate delegate = new MoveCallbackDelegate(callback,
+ handler.getLooper());
+ try {
+ mPM.registerMoveCallback(delegate);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ mDelegates.add(delegate);
}
}
@Override
- public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- if (app.isInternal()) {
- return Preconditions.checkNotNull(
- storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
- } else if (app.isExternalAsec()) {
- final List<VolumeInfo> vols = storage.getVolumes();
- for (VolumeInfo vol : vols) {
- if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
- return vol;
+ public void unregisterMoveCallback(MoveCallback callback) {
+ synchronized (mDelegates) {
+ for (Iterator<MoveCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
+ final MoveCallbackDelegate delegate = i.next();
+ if (delegate.mCallback == callback) {
+ try {
+ mPM.unregisterMoveCallback(delegate);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ i.remove();
}
}
- throw new IllegalStateException("Failed to find primary public volume");
- } else {
- return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid));
}
}
@Override
- public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+ public int movePackage(String packageName, VolumeInfo vol) {
+ try {
+ final String volumeUuid;
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) {
+ volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL;
+ } else if (vol.isPrimaryPhysical()) {
+ volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
+ } else {
+ volumeUuid = Preconditions.checkNotNull(vol.fsUuid);
+ }
+
+ return mPM.movePackage(packageName, volumeUuid);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ if (app.isInternal()) {
+ return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
+ } else if (app.isExternalAsec()) {
+ return storage.getPrimaryPhysicalVolume();
+ } else {
+ return storage.findVolumeByUuid(app.volumeUuid);
+ }
+ }
+
+ @Override
+ public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final VolumeInfo currentVol = getPackageCurrentVolume(app);
final List<VolumeInfo> vols = storage.getVolumes();
final List<VolumeInfo> candidates = new ArrayList<>();
for (VolumeInfo vol : vols) {
- if (isCandidateVolume(app, vol)) {
+ if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(app, vol)) {
candidates.add(vol);
}
}
return candidates;
}
- private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) {
+ private static boolean isPackageCandidateVolume(ApplicationInfo app, VolumeInfo vol) {
// Private internal is always an option
if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
return true;
@@ -1473,10 +1524,14 @@
return false;
}
- // Moving into an ASEC on public primary is only an option when app is
- // internal, or already in ASEC
- if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
- return app.isInternal() || app.isExternalAsec();
+ // Gotta be able to write there
+ if (!vol.isMountedWritable()) {
+ return false;
+ }
+
+ // Moving into an ASEC on public primary is only option internal
+ if (vol.isPrimaryPhysical()) {
+ return app.isInternal();
}
// Otherwise we can move to any private volume
@@ -1484,13 +1539,66 @@
}
@Override
- public String getInstallerPackageName(String packageName) {
+ public int movePrimaryStorage(VolumeInfo vol) {
try {
- return mPM.getInstallerPackageName(packageName);
+ final String volumeUuid;
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) {
+ volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL;
+ } else if (vol.isPrimaryPhysical()) {
+ volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
+ } else {
+ volumeUuid = Preconditions.checkNotNull(vol.fsUuid);
+ }
+
+ return mPM.movePrimaryStorage(volumeUuid);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return null;
+ }
+
+ public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final String volumeUuid = storage.getPrimaryStorageUuid();
+ if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
+ return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
+ } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
+ return storage.getPrimaryPhysicalVolume();
+ } else {
+ return storage.findVolumeByUuid(volumeUuid);
+ }
+ }
+
+ public @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final VolumeInfo currentVol = getPrimaryStorageCurrentVolume();
+ final List<VolumeInfo> vols = storage.getVolumes();
+ final List<VolumeInfo> candidates = new ArrayList<>();
+ for (VolumeInfo vol : vols) {
+ if (Objects.equals(vol, currentVol) || isPrimaryStorageCandidateVolume(vol)) {
+ candidates.add(vol);
+ }
+ }
+ return candidates;
+ }
+
+ private static boolean isPrimaryStorageCandidateVolume(VolumeInfo vol) {
+ // Private internal is always an option
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
+ return true;
+ }
+
+ // Gotta be able to write there
+ if (!vol.isMountedWritable()) {
+ return false;
+ }
+
+ // We can move to public volumes on legacy devices
+ if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.getDisk().isDefaultPrimary()) {
+ return true;
+ }
+
+ // Otherwise we can move to any private volume
+ return (vol.getType() == VolumeInfo.TYPE_PRIVATE);
}
@Override
@@ -1941,6 +2049,45 @@
return null;
}
+ /** {@hide} */
+ private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements
+ Handler.Callback {
+ private static final int MSG_STARTED = 1;
+ private static final int MSG_STATUS_CHANGED = 2;
+
+ final MoveCallback mCallback;
+ final Handler mHandler;
+
+ public MoveCallbackDelegate(MoveCallback callback, Looper looper) {
+ mCallback = callback;
+ mHandler = new Handler(looper, this);
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ final int moveId = msg.arg1;
+ switch (msg.what) {
+ case MSG_STARTED:
+ mCallback.onStarted(moveId, (String) msg.obj);
+ return true;
+ case MSG_STATUS_CHANGED:
+ mCallback.onStatusChanged(moveId, msg.arg2, (long) msg.obj);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onStarted(int moveId, String title) {
+ mHandler.obtainMessage(MSG_STARTED, moveId, 0, title).sendToTarget();
+ }
+
+ @Override
+ public void onStatusChanged(int moveId, int status, long estMillis) {
+ mHandler.obtainMessage(MSG_STATUS_CHANGED, moveId, status, estMillis).sendToTarget();
+ }
+ }
+
private final ContextImpl mContext;
private final IPackageManager mPM;
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 56cd53e..ebb3c43 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -16,6 +16,8 @@
package android.app;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.app.trust.ITrustManager;
import android.content.Context;
import android.content.Intent;
@@ -111,6 +113,7 @@
*
* @see #reenableKeyguard()
*/
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void disableKeyguard() {
try {
mWM.disableKeyguard(mToken, mTag);
@@ -132,6 +135,7 @@
*
* @see #disableKeyguard()
*/
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void reenableKeyguard() {
try {
mWM.reenableKeyguard(mToken);
@@ -302,6 +306,7 @@
* once the user has gotten past the keyguard.
*/
@Deprecated
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void exitKeyguardSecurely(final OnKeyguardExitResult callback) {
try {
mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e7f8f6d..2cf23af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5648,6 +5648,13 @@
/**
* Value to be used with {@link #setPricingInformation} to indicate that the content
+ * referred by the notification item is available currently as a pre-order, and the price
+ * value provided is the purchase price for the item.
+ */
+ public static final String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+
+ /**
+ * Value to be used with {@link #setPricingInformation} to indicate that the content
* referred by the notification item is available as part of a subscription based service,
* and the price value provided is the subscription price for the service.
*/
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a20aa668..47133d4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2651,14 +2651,12 @@
/**
* @hide
- * Sets the given package as the device owner. The package must already be installed and there
- * shouldn't be an existing device owner registered, for this call to succeed. Also, this
- * method must be called before the device is provisioned.
+ * Sets the given package as the device owner.
+ * Same as {@link #setDeviceOwner(String, String)} but without setting a device owner name.
* @param packageName the package name of the application to be registered as the device owner.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
- * @throws IllegalStateException if a device owner is already registered or the device has
- * already been provisioned.
+ * @throws IllegalStateException If the preconditions mentioned are not met.
*/
public boolean setDeviceOwner(String packageName) throws IllegalArgumentException,
IllegalStateException {
@@ -2667,15 +2665,17 @@
/**
* @hide
- * Sets the given package as the device owner. The package must already be installed and there
- * shouldn't be an existing device owner registered, for this call to succeed. Also, this
- * method must be called before the device is provisioned.
+ * Sets the given package as the device owner. The package must already be installed. There
+ * must not already be a device owner.
+ * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
+ * this method.
+ * Calling this after the setup phase of the primary user has completed is allowed only if
+ * the caller is the shell uid, and there are no additional users and no accounts.
* @param packageName the package name of the application to be registered as the device owner.
* @param ownerName the human readable name of the institution that owns this device.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
- * @throws IllegalStateException if a device owner is already registered or the device has
- * already been provisioned.
+ * @throws IllegalStateException If the preconditions mentioned are not met.
*/
public boolean setDeviceOwner(String packageName, String ownerName)
throws IllegalArgumentException, IllegalStateException {
@@ -2961,14 +2961,18 @@
/**
* @hide
* Sets the given component as the profile owner of the given user profile. The package must
- * already be installed and there shouldn't be an existing profile owner registered for this
- * user. Only the system can call this API if the user has already completed setup.
+ * already be installed. There must not already be a profile owner for this user.
+ * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
+ * this method.
+ * Calling this after the setup phase of the specified user has completed is allowed only if:
+ * - the caller is SYSTEM_UID.
+ * - or the caller is the shell uid, and there are no accounts on the specified user.
* @param admin the component name to be registered as profile owner.
* @param ownerName the human readable name of the organisation associated with this DPM.
* @param userHandle the userId to set the profile owner for.
* @return whether the component was successfully registered as the profile owner.
- * @throws IllegalArgumentException if admin is null, the package isn't installed, or
- * the user has already been set up.
+ * @throws IllegalArgumentException if admin is null, the package isn't installed, or the
+ * preconditions mentioned are not met.
*/
public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle)
throws IllegalArgumentException {
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4ed1489..23659e3 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,4 +30,6 @@
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
String callingPackage);
UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
+ void setAppIdle(String packageName, boolean idle, int userId);
+ boolean isAppIdle(String packageName, int userId);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index bc6099a..8a01d66 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import java.util.Collections;
@@ -217,4 +218,20 @@
}
return aggregatedStats;
}
+
+ /**
+ * Returns whether the specified app is currently considered idle. This will be true if the
+ * app hasn't been used directly or indirectly for a period of time defined by the system. This
+ * could be of the order of several hours or days.
+ * @param packageName The package name of the app to query
+ * @return whether the app is currently considered idle
+ */
+ public boolean isAppIdle(String packageName) {
+ try {
+ return mService.isAppIdle(packageName, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ // fall through and return default
+ }
+ return false;
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2418e82..79e560f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -792,7 +792,6 @@
// mService is null, handle that case
}
} catch (RemoteException e) {Log.e(TAG, "", e);}
- if (DBG) Log.d(TAG, "" + hashCode() + ": getState() : mService = null. Returning STATE_OFF");
return STATE_OFF;
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 447c668..ae59bfc 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -50,7 +50,6 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.IntentSender;
-import com.android.internal.os.IResultReceiver;
/**
* See {@link PackageManager} for documentation on most of the APIs
@@ -431,8 +430,13 @@
PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
- void movePackage(String packageName, IPackageMoveObserver observer, int flags);
- void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer);
+ int getMoveStatus(int moveId);
+
+ void registerMoveCallback(in IPackageMoveObserver callback);
+ void unregisterMoveCallback(in IPackageMoveObserver callback);
+
+ int movePackage(in String packageName, in String volumeUuid);
+ int movePrimaryStorage(in String volumeUuid);
boolean addPermissionAsync(in PermissionInfo info);
diff --git a/core/java/android/content/pm/IPackageMoveObserver.aidl b/core/java/android/content/pm/IPackageMoveObserver.aidl
index baa1595..50ab3b5 100644
--- a/core/java/android/content/pm/IPackageMoveObserver.aidl
+++ b/core/java/android/content/pm/IPackageMoveObserver.aidl
@@ -22,6 +22,6 @@
* @hide
*/
oneway interface IPackageMoveObserver {
- void packageMoved(in String packageName, int returnCode);
+ void onStarted(int moveId, String title);
+ void onStatusChanged(int moveId, int status, long estMillis);
}
-
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e4108b1..e1c271d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -42,6 +42,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
@@ -875,7 +876,8 @@
*
* @hide
*/
- public static final int MOVE_SUCCEEDED = 1;
+ public static final int MOVE_SUCCEEDED = -100;
+
/**
* Error code that is passed to the {@link IPackageMoveObserver} by
* {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
@@ -941,6 +943,7 @@
* been installed on external media.
* @hide
*/
+ @Deprecated
public static final int MOVE_INTERNAL = 0x00000001;
/**
@@ -948,8 +951,12 @@
* the package should be moved to external media.
* @hide
*/
+ @Deprecated
public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
+ /** {@hide} */
+ public static final String EXTRA_MOVE_ID = "android.content.pm.extra.MOVE_ID";
+
/**
* Usable by the required verifier as the {@code verificationCode} argument
* for {@link PackageManager#verifyPendingInstall} to indicate that it will
@@ -4183,17 +4190,42 @@
* @hide
*/
@Deprecated
- public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags);
+ public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ throw new UnsupportedOperationException();
+ }
/** {@hide} */
- public abstract void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer);
+ public static boolean isMoveStatusFinished(int status) {
+ return (status < 0 || status > 100);
+ }
/** {@hide} */
- public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app);
+ public static abstract class MoveCallback {
+ public abstract void onStarted(int moveId, String title);
+ public abstract void onStatusChanged(int moveId, int status, long estMillis);
+ }
/** {@hide} */
- public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app);
+ public abstract int getMoveStatus(int moveId);
+
+ /** {@hide} */
+ public abstract void registerMoveCallback(MoveCallback callback, Handler handler);
+ /** {@hide} */
+ public abstract void unregisterMoveCallback(MoveCallback callback);
+
+ /** {@hide} */
+ public abstract int movePackage(String packageName, VolumeInfo vol);
+ /** {@hide} */
+ public abstract @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app);
+ /** {@hide} */
+ public abstract @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app);
+
+ /** {@hide} */
+ public abstract int movePrimaryStorage(VolumeInfo vol);
+ /** {@hide} */
+ public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume();
+ /** {@hide} */
+ public abstract @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes();
/**
* Returns the device identity that verifiers can use to associate their scheme to a particular
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 31e6e25..0cf8df1 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -65,6 +65,12 @@
public abstract class CameraCaptureSession implements AutoCloseable {
/**
+ * Used to identify invalid session ID.
+ * @hide
+ */
+ public static final int SESSION_ID_NONE = -1;
+
+ /**
* Get the camera device that this session is created for.
*/
public abstract CameraDevice getDevice();
@@ -168,10 +174,11 @@
* @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
* configured as outputs for this session; or a reprocess
* capture request is submitted in a non-reprocessible capture
- * session; or the capture targets a Surface in the middle
- * of being {@link #prepare prepared}; or the handler is
- * null, the listener is not null, and the calling thread has
- * no looper.
+ * session; or the reprocess capture request was created with
+ * a {@link TotalCaptureResult} from a different session; or
+ * the capture targets a Surface in the middle of being
+ * {@link #prepare prepared}; or the handler is null, the
+ * listener is not null, and the calling thread has no looper.
*
* @see #captureBurst
* @see #setRepeatingRequest
@@ -226,7 +233,9 @@
* capture request is submitted in a non-reprocessible capture
* session; or the list of requests contains both requests to
* capture images from the camera and reprocess capture
- * requests; or one of the captures targets a Surface in the
+ * requests; or one of the reprocess capture requests was
+ * created with a {@link TotalCaptureResult} from a different
+ * session; or one of the captures targets a Surface in the
* middle of being {@link #prepare prepared}; or if the handler
* is null, the listener is not null, and the calling thread
* has no looper.
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 35727e8..19d17b1 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -158,6 +158,9 @@
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
private boolean mIsReprocess;
+ // Each reprocess request must be tied to a reprocessible session ID.
+ // Valid only for reprocess requests (mIsReprocess == true).
+ private int mReprocessibleSessionId;
private Object mUserTag;
@@ -170,6 +173,7 @@
mSettings = new CameraMetadataNative();
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = false;
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
/**
@@ -182,6 +186,7 @@
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
mIsReprocess = source.mIsReprocess;
+ mReprocessibleSessionId = source.mReprocessibleSessionId;
mUserTag = source.mUserTag;
}
@@ -189,11 +194,36 @@
* Take ownership of passed-in settings.
*
* Used by the Builder to create a mutable CaptureRequest.
+ *
+ * @param settings Settings for this capture request.
+ * @param isReprocess Indicates whether to create a reprocess capture request. {@code true}
+ * to create a reprocess capture request. {@code false} to create a regular
+ * capture request.
+ * @param reprocessibleSessionId The ID of the camera capture session this capture is created
+ * for. This is used to validate if the application submits a
+ * reprocess capture request to the same session where
+ * the {@link TotalCaptureResult}, used to create the reprocess
+ * capture, came from.
+ *
+ * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+ * reprocessibleSessionId.
+ *
+ * @see CameraDevice#createReprocessCaptureRequest
*/
- private CaptureRequest(CameraMetadataNative settings, boolean isReprocess) {
+ private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
+ int reprocessibleSessionId) {
mSettings = CameraMetadataNative.move(settings);
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = isReprocess;
+ if (isReprocess) {
+ if (reprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+ throw new IllegalArgumentException("Create a reprocess capture request with an " +
+ "invalid session ID: " + reprocessibleSessionId);
+ }
+ mReprocessibleSessionId = reprocessibleSessionId;
+ } else {
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
+ }
}
/**
@@ -277,6 +307,23 @@
}
/**
+ * Get the reprocessible session ID this reprocess capture request is associated with.
+ *
+ * @return the reprocessible session ID this reprocess capture request is associated with
+ *
+ * @throws IllegalStateException if this capture request is not a reprocess capture request.
+ * @hide
+ */
+ public int getReprocessibleSessionId() {
+ if (mIsReprocess == false ||
+ mReprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+ throw new IllegalStateException("Getting the reprocessible session ID for a "+
+ "non-reprocess capture request is illegal.");
+ }
+ return mReprocessibleSessionId;
+ }
+
+ /**
* Determine whether this CaptureRequest is equal to another CaptureRequest.
*
* <p>A request is considered equal to another is if it's set of key/values is equal, it's
@@ -298,7 +345,8 @@
&& Objects.equals(mUserTag, other.mUserTag)
&& mSurfaceSet.equals(other.mSurfaceSet)
&& mSettings.equals(other.mSettings)
- && mIsReprocess == other.mIsReprocess;
+ && mIsReprocess == other.mIsReprocess
+ && mReprocessibleSessionId == other.mReprocessibleSessionId;
}
@Override
@@ -347,6 +395,7 @@
}
mIsReprocess = (in.readInt() == 0) ? false : true;
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
@Override
@@ -397,10 +446,23 @@
* Initialize the builder using the template; the request takes
* ownership of the template.
*
+ * @param template Template settings for this capture request.
+ * @param reprocess Indicates whether to create a reprocess capture request. {@code true}
+ * to create a reprocess capture request. {@code false} to create a regular
+ * capture request.
+ * @param reprocessibleSessionId The ID of the camera capture session this capture is
+ * created for. This is used to validate if the application
+ * submits a reprocess capture request to the same session
+ * where the {@link TotalCaptureResult}, used to create the
+ * reprocess capture, came from.
+ *
+ * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+ * reprocessibleSessionId.
* @hide
*/
- public Builder(CameraMetadataNative template, boolean reprocess) {
- mRequest = new CaptureRequest(template, reprocess);
+ public Builder(CameraMetadataNative template, boolean reprocess,
+ int reprocessibleSessionId) {
+ mRequest = new CaptureRequest(template, reprocess, reprocessibleSessionId);
}
/**
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 6f7dd78..fb3c098 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -50,6 +50,7 @@
public final class TotalCaptureResult extends CaptureResult {
private final List<CaptureResult> mPartialResults;
+ private final int mSessionId;
/**
* Takes ownership of the passed-in camera metadata and the partial results
@@ -58,7 +59,7 @@
* @hide
*/
public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
- CaptureResultExtras extras, List<CaptureResult> partials) {
+ CaptureResultExtras extras, List<CaptureResult> partials, int sessionId) {
super(results, parent, extras);
if (partials == null) {
@@ -66,6 +67,8 @@
} else {
mPartialResults = partials;
}
+
+ mSessionId = sessionId;
}
/**
@@ -78,6 +81,7 @@
super(results, sequenceId);
mPartialResults = new ArrayList<>();
+ mSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
/**
@@ -95,4 +99,14 @@
public List<CaptureResult> getPartialResults() {
return Collections.unmodifiableList(mPartialResults);
}
+
+ /**
+ * Get the ID of the session where the capture request of this result was submitted.
+ *
+ * @return The session ID
+ * @hide
+ */
+ public int getSessionId() {
+ return mSessionId;
+ }
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index c74204d..3c19529 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -156,9 +156,10 @@
} else if (request.isReprocess() && !isReprocessible()) {
throw new IllegalArgumentException("this capture session cannot handle reprocess " +
"requests");
+ } else if (request.isReprocess() && request.getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another session");
}
-
checkNotClosed();
handler = checkHandler(handler, callback);
@@ -185,12 +186,17 @@
if (reprocess && !isReprocessible()) {
throw new IllegalArgumentException("this capture session cannot handle reprocess " +
"requests");
+ } else if (reprocess && requests.get(0).getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another session");
}
for (int i = 1; i < requests.size(); i++) {
if (requests.get(i).isReprocess() != reprocess) {
throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
" requests");
+ } else if (reprocess && requests.get(i).getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another " +
+ "session");
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1e680dfd..ff4ad79 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -585,8 +585,8 @@
return null;
}
- CaptureRequest.Builder builder =
- new CaptureRequest.Builder(templatedRequest, /*reprocess*/false);
+ CaptureRequest.Builder builder = new CaptureRequest.Builder(
+ templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
return builder;
}
@@ -601,7 +601,8 @@
CameraMetadataNative resultMetadata = new
CameraMetadataNative(inputResult.getNativeCopy());
- return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true);
+ return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
+ inputResult.getSessionId());
}
}
@@ -763,7 +764,7 @@
if (callback != null) {
mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback,
- requestList, handler, repeating));
+ requestList, handler, repeating, mNextSessionId - 1));
} else {
if (DEBUG) {
Log.d(TAG, "Listen for request " + requestId + " is null");
@@ -1095,9 +1096,10 @@
private final CaptureCallback mCallback;
private final List<CaptureRequest> mRequestList;
private final Handler mHandler;
+ private final int mSessionId;
CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
- Handler handler, boolean repeating) {
+ Handler handler, boolean repeating, int sessionId) {
if (callback == null || handler == null) {
throw new UnsupportedOperationException(
"Must have a valid handler and a valid callback");
@@ -1106,6 +1108,7 @@
mHandler = handler;
mRequestList = new ArrayList<CaptureRequest>(requestList);
mCallback = callback;
+ mSessionId = sessionId;
}
public boolean isRepeating() {
@@ -1140,6 +1143,10 @@
return mHandler;
}
+ public int getSessionId() {
+ return mSessionId;
+ }
+
}
/**
@@ -1643,8 +1650,8 @@
List<CaptureResult> partialResults =
mFrameNumberTracker.popPartialResults(frameNumber);
- final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, resultExtras, partialResults);
+ final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
+ request, resultExtras, partialResults, holder.getSessionId());
// Final capture result
resultDispatch = new Runnable() {
@@ -1665,7 +1672,8 @@
holder.getHandler().post(resultDispatch);
// Collect the partials for a total result; or mark the frame as totally completed
- mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, isReprocess);
+ mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
+ isReprocess);
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 481fc2f..1b57055 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1440,7 +1440,8 @@
void showWindowInner(boolean showInput) {
boolean doShowInput = false;
- boolean wasVisible = mWindowVisible;
+ final int previousImeWindowStatus =
+ (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
mWindowVisible = true;
if (!mShowInputRequested) {
if (mInputStarted) {
@@ -1485,9 +1486,12 @@
startExtractingText(false);
}
- if (!wasVisible) {
+ final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
+ if (previousImeWindowStatus != nextImeWindowStatus) {
+ mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
+ }
+ if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
- mImm.setImeWindowStatus(mToken, IME_ACTIVE, mBackDisposition);
onWindowShown();
mWindow.show();
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index b302f95..931cd3e 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,11 +16,13 @@
package android.os;
+import android.provider.DocumentsContract.Document;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import android.webkit.MimeTypeMap;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@@ -34,6 +36,7 @@
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@@ -533,4 +536,76 @@
}
return null;
}
+
+ /**
+ * Generates a unique file name under the given parent directory. If the display name doesn't
+ * have an extension that matches the requested MIME type, the default extension for that MIME
+ * type is appended. If a file already exists, the name is appended with a numerical value to
+ * make it unique.
+ *
+ * For example, the display name 'example' with 'text/plain' MIME might produce
+ * 'example.txt' or 'example (1).txt', etc.
+ *
+ * @throws FileNotFoundException
+ */
+ public static File buildUniqueFile(File parent, String mimeType, String displayName)
+ throws FileNotFoundException {
+ String name;
+ String ext;
+
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ name = displayName;
+ ext = null;
+ } else {
+ String mimeTypeFromExt;
+
+ // Extract requested extension from display name
+ final int lastDot = displayName.lastIndexOf('.');
+ if (lastDot >= 0) {
+ name = displayName.substring(0, lastDot);
+ ext = displayName.substring(lastDot + 1);
+ mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ ext.toLowerCase());
+ } else {
+ name = displayName;
+ ext = null;
+ mimeTypeFromExt = null;
+ }
+
+ if (mimeTypeFromExt == null) {
+ mimeTypeFromExt = "application/octet-stream";
+ }
+
+ final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
+ mimeType);
+ if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
+ // Extension maps back to requested MIME type; allow it
+ } else {
+ // No match; insist that create file matches requested MIME
+ name = displayName;
+ ext = extFromMimeType;
+ }
+ }
+
+ File file = buildFile(parent, name, ext);
+
+ // If conflicting file, try adding counter suffix
+ int n = 0;
+ while (file.exists()) {
+ if (n++ >= 32) {
+ throw new FileNotFoundException("Failed to create unique file");
+ }
+ file = buildFile(parent, name + " (" + n + ")", ext);
+ }
+
+ return file;
+ }
+
+ private static File buildFile(File parent, String name, String ext) {
+ if (TextUtils.isEmpty(ext)) {
+ return new File(parent, name);
+ } else {
+ return new File(parent, name + "." + ext);
+ }
+ }
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 43309c0..8c1f44f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -115,15 +115,15 @@
* later reading.</p>
*
* <p>There are also some methods that provide a more efficient way to work
- * with Parcelables: {@link #writeTypedArray},
- * {@link #writeTypedList(List)},
- * {@link #readTypedArray} and {@link #readTypedList}. These methods
+ * with Parcelables: {@link #writeTypedObject}, {@link #writeTypedArray},
+ * {@link #writeTypedList}, {@link #readTypedObject},
+ * {@link #createTypedArray} and {@link #createTypedArrayList}. These methods
* do not write the class information of the original object: instead, the
* caller of the read function must know what type to expect and pass in the
* appropriate {@link Parcelable.Creator Parcelable.Creator} instead to
* properly construct the new object and read its data. (To more efficient
- * write and read a single Parceable object, you can directly call
- * {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
+ * write and read a single Parceable object that is not null, you can directly
+ * call {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
* {@link Parcelable.Creator#createFromParcel Parcelable.Creator.createFromParcel}
* yourself.)</p>
*
@@ -1223,6 +1223,24 @@
}
/**
+ * Flatten the Parcelable object into the parcel.
+ *
+ * @param val The Parcelable object to be written.
+ * @param parcelableFlags Contextual flags as per
+ * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+ *
+ * @see #readTypedObject
+ */
+ public final <T extends Parcelable> void writeTypedObject(T val, int parcelableFlags) {
+ if (val != null) {
+ writeInt(1);
+ val.writeToParcel(this, parcelableFlags);
+ } else {
+ writeInt(0);
+ }
+ }
+
+ /**
* Flatten a generic object in to a parcel. The given Object value may
* currently be one of the following types:
*
@@ -2138,6 +2156,25 @@
}
/**
+ * Read and return a typed Parcelable object from a parcel.
+ * Returns null if the previous written object was null.
+ * The object <em>must</em> have previous been written via
+ * {@link #writeTypedObject} with the same object type.
+ *
+ * @return A newly created object of the type that was previously
+ * written.
+ *
+ * @see #writeTypedObject
+ */
+ public final <T> T readTypedObject(Parcelable.Creator<T> c) {
+ if (readInt() != 0) {
+ return c.createFromParcel(this);
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Write a heterogeneous array of Parcelable objects into the Parcel.
* Each object in the array is written along with its class name, so
* that the correct class can later be instantiated. As a result, this
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 64f2a05..9623695 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -36,7 +36,10 @@
* @hide
*/
public class DiskInfo implements Parcelable {
- public static final String EXTRA_DISK_ID = "android.os.storage.extra.DISK_ID";
+ public static final String ACTION_DISK_SCANNED =
+ "android.os.storage.action.DISK_SCANNED";
+ public static final String EXTRA_DISK_ID =
+ "android.os.storage.extra.DISK_ID";
public static final int FLAG_ADOPTABLE = 1 << 0;
public static final int FLAG_DEFAULT_PRIMARY = 1 << 1;
@@ -96,6 +99,14 @@
}
}
+ public boolean isAdoptable() {
+ return (flags & FLAG_ADOPTABLE) != 0;
+ }
+
+ public boolean isDefaultPrimary() {
+ return (flags & FLAG_DEFAULT_PRIMARY) != 0;
+ }
+
public boolean isSd() {
return (flags & FLAG_SD) != 0;
}
@@ -104,10 +115,6 @@
return (flags & FLAG_USB) != 0;
}
- public boolean isAdoptable() {
- return (flags & FLAG_ADOPTABLE) != 0;
- }
-
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 0a8187e..0b1031c 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1063,6 +1063,38 @@
_data.recycle();
}
}
+
+ @Override
+ public String getPrimaryStorageUuid() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ String _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_getPrimaryStorageUuid, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readString();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ @Override
+ public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(volumeUuid);
+ mRemote.transact(Stub.TRANSACTION_setPrimaryStorageUuid, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
}
private static final String DESCRIPTOR = "IMountService";
@@ -1169,6 +1201,9 @@
static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 52;
static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 53;
+ static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 54;
+ static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 55;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1669,6 +1704,20 @@
reply.writeNoException();
return true;
}
+ case TRANSACTION_getPrimaryStorageUuid: {
+ data.enforceInterface(DESCRIPTOR);
+ String volumeUuid = getPrimaryStorageUuid();
+ reply.writeNoException();
+ reply.writeString(volumeUuid);
+ return true;
+ }
+ case TRANSACTION_setPrimaryStorageUuid: {
+ data.enforceInterface(DESCRIPTOR);
+ String volumeUuid = data.readString();
+ setPrimaryStorageUuid(volumeUuid);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -1969,4 +2018,7 @@
public void setVolumeNickname(String volId, String nickname) throws RemoteException;
public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException;
+
+ public String getPrimaryStorageUuid() throws RemoteException;
+ public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException;
}
diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java
index 8e878a4..fcb4779 100644
--- a/core/java/android/os/storage/IMountServiceListener.java
+++ b/core/java/android/os/storage/IMountServiceListener.java
@@ -98,10 +98,11 @@
reply.writeNoException();
return true;
}
- case TRANSACTION_onDiskUnsupported: {
+ case TRANSACTION_onDiskScanned: {
data.enforceInterface(DESCRIPTOR);
final DiskInfo disk = (DiskInfo) data.readParcelable(null);
- onDiskUnsupported(disk);
+ final int volumeCount = data.readInt();
+ onDiskScanned(disk, volumeCount);
reply.writeNoException();
return true;
}
@@ -207,13 +208,14 @@
}
@Override
- public void onDiskUnsupported(DiskInfo disk) throws RemoteException {
+ public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeParcelable(disk, 0);
- mRemote.transact(Stub.TRANSACTION_onDiskUnsupported, _data, _reply,
+ _data.writeInt(volumeCount);
+ mRemote.transact(Stub.TRANSACTION_onDiskScanned, _data, _reply,
android.os.IBinder.FLAG_ONEWAY);
_reply.readException();
} finally {
@@ -224,12 +226,10 @@
}
static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0);
-
static final int TRANSACTION_onStorageStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 1);
-
static final int TRANSACTION_onVolumeStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_onVolumeMetadataChanged = (IBinder.FIRST_CALL_TRANSACTION + 3);
- static final int TRANSACTION_onDiskUnsupported = (IBinder.FIRST_CALL_TRANSACTION + 4);
+ static final int TRANSACTION_onDiskScanned = (IBinder.FIRST_CALL_TRANSACTION + 4);
}
/**
@@ -255,5 +255,5 @@
public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException;
- public void onDiskUnsupported(DiskInfo disk) throws RemoteException;
+ public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java
index ad2fae0..6a0140e 100644
--- a/core/java/android/os/storage/StorageEventListener.java
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -44,6 +44,6 @@
public void onVolumeMetadataChanged(VolumeInfo vol) {
}
- public void onDiskUnsupported(DiskInfo disk) {
+ public void onDiskScanned(DiskInfo disk, int volumeCount) {
}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f101352..747fb40 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -73,6 +73,11 @@
public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
/** {@hide} */
+ public static final String UUID_PRIVATE_INTERNAL = null;
+ /** {@hide} */
+ public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
+
+ /** {@hide} */
public static final int FLAG_ALL_METADATA = 1 << 0;
private final Context mContext;
@@ -89,7 +94,7 @@
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
private static final int MSG_VOLUME_METADATA_CHANGED = 3;
- private static final int MSG_DISK_UNSUPPORTED = 4;
+ private static final int MSG_DISK_SCANNED = 4;
final StorageEventListener mCallback;
final Handler mHandler;
@@ -116,8 +121,8 @@
mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
args.recycle();
return true;
- case MSG_DISK_UNSUPPORTED:
- mCallback.onDiskUnsupported((DiskInfo) args.arg1);
+ case MSG_DISK_SCANNED:
+ mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
args.recycle();
return true;
}
@@ -156,10 +161,11 @@
}
@Override
- public void onDiskUnsupported(DiskInfo disk) {
+ public void onDiskScanned(DiskInfo disk, int volumeCount) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = disk;
- mHandler.obtainMessage(MSG_DISK_UNSUPPORTED, args).sendToTarget();
+ args.argi2 = volumeCount;
+ mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
}
}
@@ -534,17 +540,26 @@
/** {@hide} */
public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
String descrip = vol.getDescription();
-
if (vol.disk != null) {
if (TextUtils.isEmpty(descrip)) {
descrip = vol.disk.getDescription();
}
}
-
return descrip;
}
/** {@hide} */
+ public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
+ final List<VolumeInfo> vols = getVolumes();
+ for (VolumeInfo vol : vols) {
+ if (vol.isPrimaryPhysical()) {
+ return vol;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
public void mount(String volId) {
try {
mMountService.mount(volId);
@@ -628,6 +643,24 @@
}
/** {@hide} */
+ public String getPrimaryStorageUuid() {
+ try {
+ return mMountService.getPrimaryStorageUuid();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void setPrimaryStorageUuid(String volumeUuid) {
+ try {
+ mMountService.setPrimaryStorageUuid(volumeUuid);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
public @Nullable StorageVolume getStorageVolume(File file) {
return getStorageVolume(getVolumeList(), file);
}
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index f3498d5..4e9cfc7 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -242,6 +242,10 @@
return (mountFlags & MOUNT_FLAG_PRIMARY) != 0;
}
+ public boolean isPrimaryPhysical() {
+ return isPrimary() && (getType() == TYPE_PUBLIC);
+ }
+
public boolean isVisible() {
return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
}
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 69a05c4..bae06b8 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.RequiresPermission;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -32,6 +33,9 @@
import android.util.Log;
import android.webkit.WebIconDatabase;
+import static android.Manifest.permission.READ_HISTORY_BOOKMARKS;
+import static android.Manifest.permission.WRITE_HISTORY_BOOKMARKS;
+
public class Browser {
private static final String LOGTAG = "browser";
@@ -41,6 +45,8 @@
* {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
* requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
*/
+ @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
/**
@@ -122,6 +128,8 @@
* {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
* requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
*/
+ @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri SEARCHES_URI = Uri.parse("content://browser/searches");
/**
@@ -233,6 +241,7 @@
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final Cursor getAllBookmarks(ContentResolver cr) throws
IllegalStateException {
return cr.query(Bookmarks.CONTENT_URI,
@@ -248,6 +257,7 @@
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final Cursor getAllVisitedUrls(ContentResolver cr) throws
IllegalStateException {
return cr.query(Combined.CONTENT_URI,
@@ -308,6 +318,7 @@
* @param real If true, this is an actual visit, and should add to the
* number of visits. If false, the user entered it manually.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void updateVisitedHistory(ContentResolver cr,
String url, boolean real) {
long now = System.currentTimeMillis();
@@ -358,6 +369,7 @@
* @param cr The ContentResolver used to access the database.
* @hide pending API council approval
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final String[] getVisitedHistory(ContentResolver cr) {
Cursor c = null;
String[] str = null;
@@ -393,6 +405,7 @@
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void truncateHistory(ContentResolver cr) {
// TODO make a single request to the provider to do this in a single transaction
Cursor cursor = null;
@@ -424,6 +437,7 @@
* @param cr The ContentResolver used to access the database.
* @return boolean True if the history can be cleared.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final boolean canClearHistory(ContentResolver cr) {
Cursor cursor = null;
boolean ret = false;
@@ -446,6 +460,7 @@
* Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void clearHistory(ContentResolver cr) {
deleteHistoryWhere(cr, null);
}
@@ -461,6 +476,7 @@
* @param whereClause String to limit the items affected.
* null means all items.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
private static final void deleteHistoryWhere(ContentResolver cr, String whereClause) {
Cursor cursor = null;
try {
@@ -486,6 +502,7 @@
* @param end Last date to remove. If -1, all dates after begin.
* Non-inclusive.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void deleteHistoryTimeFrame(ContentResolver cr,
long begin, long end) {
String whereClause;
@@ -511,6 +528,7 @@
* @param cr The ContentResolver used to access the database.
* @param url url to remove.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void deleteFromHistory(ContentResolver cr,
String url) {
cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url });
@@ -523,6 +541,7 @@
* @param cr The ContentResolver used to access the database.
* @param search The string to add to the searches database.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void addSearchUrl(ContentResolver cr, String search) {
// The content provider will take care of updating existing searches instead of duplicating
ContentValues values = new ContentValues();
@@ -536,6 +555,7 @@
* Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void clearSearches(ContentResolver cr) {
// FIXME: Should this clear the urls to which these searches lead?
// (i.e. remove google.com/query= blah blah blah)
@@ -557,6 +577,7 @@
* @param listener IconListener that gets the icons once they are
* retrieved.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final void requestAllIcons(ContentResolver cr, String where,
WebIconDatabase.IconListener listener) {
// Do nothing: this is no longer used.
diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java
index d21cc3c..f0ca276 100644
--- a/core/java/android/service/chooser/ChooserTarget.java
+++ b/core/java/android/service/chooser/ChooserTarget.java
@@ -78,7 +78,8 @@
* <p>The creator of a target may supply a ranking score. This score is assumed to be relative
* to the other targets supplied by the same
* {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
- * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p>
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
*
* <p>Before being sent, the PendingIntent supplied will be
* {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
@@ -113,7 +114,8 @@
* <p>The creator of a target may supply a ranking score. This score is assumed to be relative
* to the other targets supplied by the same
* {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
- * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p>
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
*
* <p>Before being sent, the IntentSender supplied will be
* {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
@@ -144,6 +146,32 @@
mIntentSender = intentSender;
}
+ /**
+ * Construct a deep link target for presentation by a chooser UI.
+ *
+ * <p>A target is composed of a title and an icon for presentation to the user.
+ * The UI presenting this target may truncate the title if it is too long to be presented
+ * in the available space, as well as crop, resize or overlay the supplied icon.</p>
+ *
+ * <p>The creator of a target may supply a ranking score. This score is assumed to be relative
+ * to the other targets supplied by the same
+ * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
+ *
+ * <p>Before being sent, the Intent supplied will be
+ * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
+ * to the chooser.</p>
+ *
+ * <p>Take care not to place custom {@link android.os.Parcelable} types into
+ * the Intent as extras, as the system will not be able to unparcel it to merge
+ * additional extras.</p>
+ *
+ * @param title title of this target that will be shown to a user
+ * @param icon icon to represent this target
+ * @param score ranking score for this target between 0.0f and 1.0f, inclusive
+ * @param intent Intent to fill in and send if the user chooses this target
+ */
public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) {
mTitle = title;
mIcon = icon;
@@ -358,6 +386,10 @@
}
dest.writeFloat(mScore);
IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest);
+ dest.writeInt(mIntent != null ? 1 : 0);
+ if (mIntent != null) {
+ mIntent.writeToParcel(dest, 0);
+ }
}
public static final Creator<ChooserTarget> CREATOR
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 0417921..4d1209a 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,7 +16,6 @@
package android.view.animation;
-import android.content.res.Configuration;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -45,16 +44,6 @@
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;
- private static final float RECOMMENDED_FIELD_OF_VIEW_FOR_TV = 40f;
- private static final float ESTIMATED_VIEWING_DISTANCE_FOR_WATCH = 11f;
- private static final float AVERAGE_VIEWING_DISTANCE_FOR_PHONES = 14.2f;
- private static final float N5_DIAGONAL_VIEW_ANGLE = 19.58f;
- private static final float N5_DENSITY = 3.0f;
- private static final float N5_DPI = 443f;
-
- private static final float COTANGENT_OF_HALF_TV_ANGLE = (float) (1 / Math.tan(Math.toRadians
- (RECOMMENDED_FIELD_OF_VIEW_FOR_TV / 2)));
-
/**
* Returns the current animation time in milliseconds. This time should be used when invoking
@@ -378,78 +367,4 @@
}
return interpolator;
}
-
- /**
- * Derives the viewing distance of a device based on the device size (in inches), and the
- * device type.
- * @hide
- */
- public static float getViewingDistance(float width, float height, int uiMode) {
- if (uiMode == Configuration.UI_MODE_TYPE_TELEVISION) {
- // TV
- return (width / 2) * COTANGENT_OF_HALF_TV_ANGLE;
- } else if (uiMode == Configuration.UI_MODE_TYPE_WATCH) {
- // Watch
- return ESTIMATED_VIEWING_DISTANCE_FOR_WATCH;
- } else {
- // Tablet, phone, etc
- return AVERAGE_VIEWING_DISTANCE_FOR_PHONES;
- }
- }
-
- /**
- * Calculates the duration scaling factor of an animation based on the hint that the animation
- * will move across the entire screen. A scaling factor of 1 means the duration on this given
- * device will be the same as the duration set through
- * {@link android.animation.Animator#setDuration(long)}. The calculation uses Nexus 5 as a
- * baseline device. That is, the duration of the animation on a given device will scale its
- * duration so that it has the same look and feel as the animation on Nexus 5. In order to
- * achieve the same perceived effect of the animation across different devices, we maintain
- * the same angular speed of the same animation in users' field of view. Therefore, the
- * duration scale factor is determined by the ratio of the angular movement on current
- * devices to that on the baseline device.
- *
- * @param width width of the screen (in inches)
- * @param height height of the screen (in inches)
- * @param viewingDistance the viewing distance of the device (i.e. watch, phone, TV, etc) in
- * inches
- * @return scaling factor (or multiplier) of the duration set through
- * {@link android.animation.Animator#setDuration(long)} on current device.
- * @hide
- */
- public static float getScreenSizeBasedDurationScale(float width, float height,
- float viewingDistance) {
- // Animation's moving distance is proportional to the screen size.
- float diagonal = (float) Math.sqrt(width * width + height * height);
- float diagonalViewAngle = (float) Math.toDegrees(Math.atan((diagonal / 2f)
- / viewingDistance) * 2);
- return diagonalViewAngle / N5_DIAGONAL_VIEW_ANGLE;
- }
-
- /**
- * Calculates the duration scaling factor of an animation under the assumption that the
- * animation is defined to move the same amount of distance (in dp) across all devices. A
- * scaling factor of 1 means the duration on this given device will be the same as the
- * duration set through {@link android.animation.Animator#setDuration(long)}. The calculation
- * uses Nexus 5 as a baseline device. That is, the duration of the animation on a given
- * device will scale its duration so that it has the same look and feel as the animation on
- * Nexus 5. In order to achieve the same perceived effect of the animation across different
- * devices, we maintain the same angular velocity of the same animation in users' field of
- * view. Therefore, the duration scale factor is determined by the ratio of the angular
- * movement on current devices to that on the baseline device.
- *
- * @param density logical density of the display. {@link android.util.DisplayMetrics#density}
- * @param dpi pixels per inch
- * @param viewingDistance viewing distance of the device (in inches)
- * @return the scaling factor of duration
- * @hide
- */
- public static float getDpBasedDurationScale(float density, float dpi,
- float viewingDistance) {
- // Angle in users' field of view per dp:
- float anglePerDp = (float) Math.atan2((density / dpi) / 2, viewingDistance) * 2;
- float baselineAnglePerDp = (float) Math.atan2((N5_DENSITY / N5_DPI) / 2,
- AVERAGE_VIEWING_DISTANCE_FOR_PHONES) * 2;
- return anglePerDp / baselineAnglePerDp;
- }
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 3340c73..9782d72 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -76,6 +76,18 @@
private static boolean sAddressSpaceReserved = false;
private static PackageInfo sPackageInfo;
+ // Error codes for loadWebViewNativeLibraryFromPackage
+ public static final int LIBLOAD_SUCCESS = 0;
+ public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
+ public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2;
+ public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3;
+ public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4;
+
+ // native relro loading error codes
+ public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5;
+ public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6;
+ public static final int LIBLOAD_FAILED_JNI_CALL = 7;
+
private static class MissingWebViewPackageException extends AndroidRuntimeException {
public MissingWebViewPackageException(String message) { super(message); }
public MissingWebViewPackageException(Exception e) { super(e); }
@@ -136,6 +148,18 @@
return sPackageInfo;
}
+ /**
+ * Load the native library for the given package name iff that package
+ * name is the same as the one providing the current webview.
+ */
+ public static int loadWebViewNativeLibraryFromPackage(String packageName) {
+ sPackageInfo = findPreferredWebViewPackage();
+ if (packageName != null && packageName.equals(sPackageInfo.packageName)) {
+ return loadNativeLibrary();
+ }
+ return LIBLOAD_WRONG_PACKAGE_NAME;
+ }
+
static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
// For now the main purpose of this function (and the factory abstraction) is to keep
@@ -434,32 +458,34 @@
}
}
- private static void loadNativeLibrary() {
+ private static int loadNativeLibrary() {
if (!sAddressSpaceReserved) {
Log.e(LOGTAG, "can't load with relro file; address space not reserved");
- return;
+ return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
}
try {
getUpdateService().waitForRelroCreationCompleted(VMRuntime.getRuntime().is64Bit());
} catch (RemoteException e) {
Log.e(LOGTAG, "error waiting for relro creation, proceeding without", e);
- return;
+ return LIBLOAD_FAILED_WAITING_FOR_RELRO;
}
try {
String[] args = getWebViewNativeLibraryPaths();
- boolean result = nativeLoadWithRelroFile(args[0] /* path32 */,
+ int result = nativeLoadWithRelroFile(args[0] /* path32 */,
args[1] /* path64 */,
CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
- if (!result) {
+ if (result != LIBLOAD_SUCCESS) {
Log.w(LOGTAG, "failed to load with relro file, proceeding without");
} else if (DEBUG) {
Log.v(LOGTAG, "loaded with relro file");
}
+ return result;
} catch (MissingWebViewPackageException e) {
Log.e(LOGTAG, "Failed to list WebView package libraries for loadNativeLibrary", e);
+ return LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
}
}
@@ -470,6 +496,6 @@
private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
private static native boolean nativeCreateRelroFile(String lib32, String lib64,
String relro32, String relro64);
- private static native boolean nativeLoadWithRelroFile(String lib32, String lib64,
+ private static native int nativeLoadWithRelroFile(String lib32, String lib64,
String relro32, String relro64);
}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index c6b4d7e..113e597 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -196,9 +196,23 @@
}
@Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ requestLayout();
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final ImageButton leftButton = mPrevButton;
- final ImageButton rightButton = mNextButton;
+ final ImageButton leftButton;
+ final ImageButton rightButton;
+ if (isLayoutRtl()) {
+ leftButton = mNextButton;
+ rightButton = mPrevButton;
+ } else {
+ leftButton = mPrevButton;
+ rightButton = mNextButton;
+ }
final int width = right - left;
final int height = bottom - top;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b049e49..652fff2 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1957,6 +1957,9 @@
if (mPositionListener != null) {
mPositionListener.onScrollChanged();
}
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.invalidateContentRect();
+ }
}
/**
@@ -3085,10 +3088,12 @@
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
- menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
- setAlphabeticShortcut('a').
- setShowAsAction(
- MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ if (canSelectText() && !hasPasswordTransformationMethod()) {
+ menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+ setAlphabeticShortcut('a').
+ setShowAsAction(
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
updateReplaceItem(menu);
}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 9ecdc9c..c959774 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1854,20 +1854,19 @@
moved = true;
}
break;
- case FOCUS_LEFT:
- if (selectedPosition > startOfRowPos) {
- mLayoutMode = LAYOUT_MOVE_SELECTION;
- setSelectionInt(Math.max(0, selectedPosition - 1));
- moved = true;
- }
- break;
- case FOCUS_RIGHT:
- if (selectedPosition < endOfRowPos) {
- mLayoutMode = LAYOUT_MOVE_SELECTION;
- setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
- moved = true;
- }
- break;
+ }
+
+ final boolean isLayoutRtl = isLayoutRtl();
+ if (selectedPosition > startOfRowPos && ((direction == FOCUS_LEFT && !isLayoutRtl) ||
+ (direction == FOCUS_RIGHT && isLayoutRtl))) {
+ mLayoutMode = LAYOUT_MOVE_SELECTION;
+ setSelectionInt(Math.max(0, selectedPosition - 1));
+ moved = true;
+ } else if (selectedPosition < endOfRowPos && ((direction == FOCUS_LEFT && isLayoutRtl) ||
+ (direction == FOCUS_RIGHT && !isLayoutRtl))) {
+ mLayoutMode = LAYOUT_MOVE_SELECTION;
+ setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
+ moved = true;
}
if (moved) {
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 0249c22..2778f0f 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -162,7 +162,6 @@
mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
- setClickable(true);
initPaints(res);
}
@@ -318,7 +317,8 @@
final int x = (int) (event.getX() + 0.5f);
final int y = (int) (event.getY() + 0.5f);
- switch (event.getAction()) {
+ final int action = event.getAction();
+ switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
final int touchedItem = getDayAtLocation(x, y);
@@ -326,6 +326,10 @@
mTouchedItem = touchedItem;
invalidate();
}
+ if (action == MotionEvent.ACTION_DOWN && touchedItem < 0) {
+ // Touch something that's not an item, reject event.
+ return false;
+ }
break;
case MotionEvent.ACTION_UP:
@@ -376,9 +380,16 @@
for (int col = 0; col < DAYS_IN_WEEK; col++) {
final int colCenter = colWidth * col + colWidth / 2;
+ final int colCenterRtl;
+ if (isLayoutRtl()) {
+ colCenterRtl = mPaddedWidth - colCenter;
+ } else {
+ colCenterRtl = colCenter;
+ }
+
final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
final String label = getDayOfWeekLabel(dayOfWeek);
- canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p);
+ canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p);
}
}
@@ -402,6 +413,13 @@
for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) {
final int colCenter = colWidth * col + colWidth / 2;
+ final int colCenterRtl;
+ if (isLayoutRtl()) {
+ colCenterRtl = mPaddedWidth - colCenter;
+ } else {
+ colCenterRtl = colCenter;
+ }
+
int stateMask = 0;
if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
@@ -413,12 +431,12 @@
stateMask |= StateSet.VIEW_STATE_ACTIVATED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
+ canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
} else if (mTouchedItem == day) {
stateMask |= StateSet.VIEW_STATE_PRESSED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
+ canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
}
final boolean isDayToday = mToday == day;
@@ -431,7 +449,7 @@
}
p.setColor(dayTextColor);
- canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p);
+ canvas.drawText(Integer.toString(day), colCenterRtl, rowCenter - halfLineHeight, p);
col++;
@@ -583,6 +601,13 @@
}
@Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ requestLayout();
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (!changed) {
return;
@@ -657,8 +682,16 @@
return -1;
}
+ // Adjust for RTL after applying padding.
+ final int paddedXRtl;
+ if (isLayoutRtl()) {
+ paddedXRtl = mPaddedWidth - paddedX;
+ } else {
+ paddedXRtl = paddedX;
+ }
+
final int row = (paddedY - headerHeight) / mDayHeight;
- final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+ final int col = (paddedXRtl * DAYS_IN_WEEK) / mPaddedWidth;
final int index = col + row * DAYS_IN_WEEK;
final int day = index + 1 - findDayOffset();
if (day < 1 || day > mDaysInMonth) {
@@ -681,10 +714,15 @@
final int index = id - 1 + findDayOffset();
- // Compute left edge.
+ // Compute left edge, taking into account RTL.
final int col = index % DAYS_IN_WEEK;
final int colWidth = mCellWidth;
- final int left = getPaddingLeft() + col * colWidth;
+ final int left;
+ if (isLayoutRtl()) {
+ left = getWidth() - getPaddingRight() - (col + 1) * colWidth;
+ } else {
+ left = getPaddingLeft() + col * colWidth;
+ }
// Compute top edge.
final int row = index / DAYS_IN_WEEK;
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index f7e9648..c0c8aec 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -403,7 +403,7 @@
// No longer care about configuration changes
mContext.unregisterReceiver(mConfigurationChangedReceiver);
- mWindowManager.removeView(mContainer);
+ mWindowManager.removeViewImmediate(mContainer);
mHandler.removeCallbacks(mPostedVisibleInitializer);
if (mCallback != null) {
@@ -490,7 +490,7 @@
setVisible(false);
return true;
}
-
+
} else {
dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8403e77..a6c39e6 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -401,6 +401,11 @@
}
@Override
+ public boolean shouldGetResolvedFilter() {
+ return true;
+ }
+
+ @Override
public int getCount() {
int count = super.getCount();
if (mServiceTargets != null) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3cd69a1..7f51d92 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1062,7 +1062,7 @@
} else {
currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent,
PackageManager.MATCH_DEFAULT_ONLY
- | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0)
+ | (shouldGetResolvedFilter() ? PackageManager.GET_RESOLVED_FILTER : 0)
| (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0)
);
// Filter out any activities that the launched uid does not
@@ -1188,6 +1188,10 @@
// This space for rent
}
+ public boolean shouldGetResolvedFilter() {
+ return mFilterLastUsed;
+ }
+
private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
CharSequence roLabel) {
// Process labels from start to i
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 414b7bc..b692a18 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -24,6 +24,12 @@
*/
public class Preconditions {
+ public static void checkArgument(boolean expression) {
+ if (!expression) {
+ throw new IllegalArgumentException();
+ }
+ }
+
/**
* Ensures that an object reference passed as a parameter to the calling
* method is not null.
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 89990c2..191662c 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -111,7 +111,7 @@
}
private void init(Context context) {
- ViewConfiguration vc = ViewConfiguration.get(getContext());
+ ViewConfiguration vc = ViewConfiguration.get(context);
mSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
@@ -290,7 +290,7 @@
float deltaX = ev.getRawX() - mDownX;
float deltaY = ev.getRawY() - mDownY;
if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) {
- mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2;
+ mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < Math.abs(deltaX);
} else {
mSwiping = false;
}
@@ -299,9 +299,9 @@
private void updateDismiss(MotionEvent ev) {
float deltaX = ev.getRawX() - mDownX;
+ mVelocityTracker.addMovement(ev);
+ mVelocityTracker.computeCurrentVelocity(1000);
if (!mDismissed) {
- mVelocityTracker.addMovement(ev);
- mVelocityTracker.computeCurrentVelocity(1000);
if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
ev.getRawX() >= mLastX) {
@@ -311,7 +311,9 @@
// Check if the user tried to undo this.
if (mDismissed && mSwiping) {
// Check if the user's finger is actually back
- if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO)) {
+ if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) ||
+ // or user is flinging back left
+ mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
mDismissed = false;
}
}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 5c08daf..441e640 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -27,9 +27,9 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.view.FocusFinder;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -43,7 +43,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.accessibility.AccessibilityRecord;
import android.view.animation.Interpolator;
import android.widget.EdgeEffect;
import android.widget.Scroller;
@@ -84,8 +83,9 @@
*/
public class ViewPager extends ViewGroup {
private static final String TAG = "ViewPager";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
+ private static final int MAX_SCROLL_X = 2 << 23;
private static final boolean USE_CACHE = false;
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
@@ -108,9 +108,13 @@
static class ItemInfo {
Object object;
- int position;
boolean scrolling;
float widthFactor;
+
+ /** Logical position of the item within the pager adapter. */
+ int position;
+
+ /** Offset between the starting edges of the item and its container. */
float offset;
}
@@ -146,6 +150,12 @@
private int mTopPageBounds;
private int mBottomPageBounds;
+ /**
+ * The increment used to move in the "left" direction. Dependent on layout
+ * direction.
+ */
+ private int mLeftIncr = -1;
+
// Offsets of the first and last items, if known.
// Set during population, used to determine if we are at the beginning
// or end of the pager data set during touch scrolling.
@@ -198,14 +208,10 @@
// "catching" the flinging pager.
private static final int CLOSE_ENOUGH = 2; // dp
- private boolean mFakeDragging;
- private long mFakeDragBeginTime;
-
private final EdgeEffect mLeftEdge;
private final EdgeEffect mRightEdge;
private boolean mFirstLayout = true;
- private boolean mNeedCalculatePageOffsets = false;
private boolean mCalledSuper;
private int mDecorChildCount;
@@ -473,7 +479,7 @@
mAdapterChangeListener = listener;
}
- private int getClientWidth() {
+ private int getPaddedWidth() {
return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}
@@ -504,36 +510,33 @@
return mCurItem;
}
- void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
- setCurrentItemInternal(item, smoothScroll, always, 0);
+ boolean setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
+ return setCurrentItemInternal(item, smoothScroll, always, 0);
}
- void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
+ boolean setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
if (mAdapter == null || mAdapter.getCount() <= 0) {
setScrollingCacheEnabled(false);
- return;
- }
- if (!always && mCurItem == item && mItems.size() != 0) {
- setScrollingCacheEnabled(false);
- return;
+ return false;
}
- if (item < 0) {
- item = 0;
- } else if (item >= mAdapter.getCount()) {
- item = mAdapter.getCount() - 1;
+ item = MathUtils.constrain(item, 0, mAdapter.getCount() - 1);
+ if (!always && mCurItem == item && mItems.size() != 0) {
+ setScrollingCacheEnabled(false);
+ return false;
}
+
final int pageLimit = mOffscreenPageLimit;
if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
// We are doing a jump by more than one page. To avoid
// glitches, we want to keep all current pages in the view
// until the scroll ends.
- for (int i=0; i<mItems.size(); i++) {
+ for (int i = 0; i < mItems.size(); i++) {
mItems.get(i).scrolling = true;
}
}
- final boolean dispatchSelected = mCurItem != item;
+ final boolean dispatchSelected = mCurItem != item;
if (mFirstLayout) {
// We don't have any idea how big we are yet and shouldn't have any pages either.
// Just set things up and let the pending layout handle things.
@@ -549,38 +552,55 @@
populate(item);
scrollToItem(item, smoothScroll, velocity, dispatchSelected);
}
+
+ return true;
}
- private void scrollToItem(int item, boolean smoothScroll, int velocity,
+ private void scrollToItem(int position, boolean smoothScroll, int velocity,
boolean dispatchSelected) {
- final ItemInfo curInfo = infoForPosition(item);
- int destX = 0;
- if (curInfo != null) {
- final int width = getClientWidth();
- destX = (int) (width * Math.max(mFirstOffset,
- Math.min(curInfo.offset, mLastOffset)));
- }
+ final int destX = getLeftEdgeForItem(position);
+
if (smoothScroll) {
smoothScrollTo(destX, 0, velocity);
+
if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
+ mOnPageChangeListener.onPageSelected(position);
}
if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ mInternalPageChangeListener.onPageSelected(position);
}
} else {
if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
+ mOnPageChangeListener.onPageSelected(position);
}
if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ mInternalPageChangeListener.onPageSelected(position);
}
+
completeScroll(false);
scrollTo(destX, 0);
pageScrolled(destX);
}
}
+ private int getLeftEdgeForItem(int position) {
+ final ItemInfo info = infoForPosition(position);
+ if (info == null) {
+ return 0;
+ }
+
+ final int width = getPaddedWidth();
+ final int scaledOffset = (int) (width * MathUtils.constrain(
+ info.offset, mFirstOffset, mLastOffset));
+
+ if (isLayoutRtl()) {
+ final int itemWidth = (int) (width * info.widthFactor + 0.5f);
+ return MAX_SCROLL_X - itemWidth - scaledOffset;
+ } else {
+ return scaledOffset;
+ }
+ }
+
/**
* Set a listener that will be invoked whenever the page changes or is incrementally
* scrolled. See {@link OnPageChangeListener}.
@@ -784,7 +804,7 @@
setScrollingCacheEnabled(true);
setScrollState(SCROLL_STATE_SETTLING);
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final int halfWidth = width / 2;
final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
final float distance = halfWidth + halfWidth *
@@ -968,7 +988,7 @@
float extraWidthLeft = 0.f;
int itemIndex = curIndex - 1;
ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
- final int clientWidth = getClientWidth();
+ final int clientWidth = getPaddedWidth();
final float leftWidthNeeded = clientWidth <= 0 ? 0 :
2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
for (int pos = mCurItem - 1; pos >= 0; pos--) {
@@ -981,7 +1001,7 @@
mAdapter.destroyItem(this, pos, ii.object);
if (DEBUG) {
Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
- " view: " + ((View) ii.object));
+ " view: " + ii.object);
}
itemIndex--;
curIndex--;
@@ -1015,7 +1035,7 @@
mAdapter.destroyItem(this, pos, ii.object);
if (DEBUG) {
Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
- " view: " + ((View) ii.object));
+ " view: " + ii.object);
}
ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
}
@@ -1099,49 +1119,51 @@
private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
final int N = mAdapter.getCount();
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+
// Fix up offsets for later layout.
if (oldCurInfo != null) {
final int oldCurPosition = oldCurInfo.position;
+
// Base offsets off of oldCurInfo.
if (oldCurPosition < curItem.position) {
int itemIndex = 0;
- ItemInfo ii = null;
float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;
- for (int pos = oldCurPosition + 1;
- pos <= curItem.position && itemIndex < mItems.size(); pos++) {
- ii = mItems.get(itemIndex);
+ for (int pos = oldCurPosition + 1; pos <= curItem.position && itemIndex < mItems.size(); pos++) {
+ ItemInfo ii = mItems.get(itemIndex);
while (pos > ii.position && itemIndex < mItems.size() - 1) {
itemIndex++;
ii = mItems.get(itemIndex);
}
+
while (pos < ii.position) {
// We don't have an item populated for this,
// ask the adapter for an offset.
offset += mAdapter.getPageWidth(pos) + marginOffset;
pos++;
}
+
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
} else if (oldCurPosition > curItem.position) {
int itemIndex = mItems.size() - 1;
- ItemInfo ii = null;
float offset = oldCurInfo.offset;
- for (int pos = oldCurPosition - 1;
- pos >= curItem.position && itemIndex >= 0; pos--) {
- ii = mItems.get(itemIndex);
+ for (int pos = oldCurPosition - 1; pos >= curItem.position && itemIndex >= 0; pos--) {
+ ItemInfo ii = mItems.get(itemIndex);
while (pos < ii.position && itemIndex > 0) {
itemIndex--;
ii = mItems.get(itemIndex);
}
+
while (pos > ii.position) {
// We don't have an item populated for this,
// ask the adapter for an offset.
offset -= mAdapter.getPageWidth(pos) + marginOffset;
pos--;
}
+
offset -= ii.widthFactor + marginOffset;
ii.offset = offset;
}
@@ -1155,6 +1177,7 @@
mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
mLastOffset = curItem.position == N - 1 ?
curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;
+
// Previous pages
for (int i = curIndex - 1; i >= 0; i--, pos--) {
final ItemInfo ii = mItems.get(i);
@@ -1165,8 +1188,10 @@
ii.offset = offset;
if (ii.position == 0) mFirstOffset = offset;
}
+
offset = curItem.offset + curItem.widthFactor + marginOffset;
pos = curItem.position + 1;
+
// Next pages
for (int i = curIndex + 1; i < itemCount; i++, pos++) {
final ItemInfo ii = mItems.get(i);
@@ -1179,8 +1204,6 @@
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
-
- mNeedCalculatePageOffsets = false;
}
/**
@@ -1546,34 +1569,47 @@
// Page views. Do this once we have the right padding offsets from above.
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- ItemInfo ii;
- if (!lp.isDecor && (ii = infoForChild(child)) != null) {
- int loff = (int) (childWidth * ii.offset);
- int childLeft = paddingLeft + loff;
- int childTop = paddingTop;
- if (lp.needsMeasure) {
- // This was added during layout and needs measurement.
- // Do it now that we know what we're working with.
- lp.needsMeasure = false;
- final int widthSpec = MeasureSpec.makeMeasureSpec(
- (int) (childWidth * lp.widthFactor),
- MeasureSpec.EXACTLY);
- final int heightSpec = MeasureSpec.makeMeasureSpec(
- (int) (height - paddingTop - paddingBottom),
- MeasureSpec.EXACTLY);
- child.measure(widthSpec, heightSpec);
- }
- if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
- + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
- + "x" + child.getMeasuredHeight());
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
- }
+ if (child.getVisibility() == GONE) {
+ continue;
}
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.isDecor) {
+ continue;
+ }
+
+ final ItemInfo ii = infoForChild(child);
+ if (ii == null) {
+ continue;
+ }
+
+ if (lp.needsMeasure) {
+ // This was added during layout and needs measurement.
+ // Do it now that we know what we're working with.
+ lp.needsMeasure = false;
+ final int widthSpec = MeasureSpec.makeMeasureSpec(
+ (int) (childWidth * lp.widthFactor),
+ MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(
+ (int) (height - paddingTop - paddingBottom),
+ MeasureSpec.EXACTLY);
+ child.measure(widthSpec, heightSpec);
+ }
+
+ final int childMeasuredWidth = child.getMeasuredWidth();
+ final int startOffset = (int) (childWidth * ii.offset);
+ final int childLeft;
+ if (isLayoutRtl()) {
+ childLeft = MAX_SCROLL_X - paddingRight - startOffset - childMeasuredWidth;
+ } else {
+ childLeft = paddingLeft + startOffset;
+ }
+
+ final int childTop = paddingTop;
+ child.layout(childLeft, childTop, childLeft + childMeasuredWidth,
+ childTop + child.getMeasuredHeight());
}
+
mTopPageBounds = paddingTop;
mBottomPageBounds = height - paddingBottom;
mDecorChildCount = decorCount;
@@ -1587,13 +1623,14 @@
@Override
public void computeScroll() {
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
- int oldX = getScrollX();
- int oldY = getScrollY();
- int x = mScroller.getCurrX();
- int y = mScroller.getCurrY();
+ final int oldX = getScrollX();
+ final int oldY = getScrollY();
+ final int x = mScroller.getCurrX();
+ final int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
scrollTo(x, y);
+
if (!pageScrolled(x)) {
mScroller.abortAnimation();
scrollTo(0, y);
@@ -1609,7 +1646,7 @@
completeScroll(true);
}
- private boolean pageScrolled(int xpos) {
+ private boolean pageScrolled(int scrollX) {
if (mItems.size() == 0) {
mCalledSuper = false;
onPageScrolled(0, 0, 0);
@@ -1619,12 +1656,21 @@
}
return false;
}
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int width = getClientWidth();
+
+ // Translate to scrollX to scrollStart for RTL.
+ final int scrollStart;
+ if (isLayoutRtl()) {
+ scrollStart = MAX_SCROLL_X - scrollX;
+ } else {
+ scrollStart = scrollX;
+ }
+
+ final ItemInfo ii = infoForFirstVisiblePage();
+ final int width = getPaddedWidth();
final int widthWithMargin = width + mPageMargin;
final float marginOffset = (float) mPageMargin / width;
final int currentPage = ii.position;
- final float pageOffset = (((float) xpos / width) - ii.offset) /
+ final float pageOffset = (((float) scrollStart / width) - ii.offset) /
(ii.widthFactor + marginOffset);
final int offsetPixels = (int) (pageOffset * widthWithMargin);
@@ -1706,7 +1752,7 @@
if (lp.isDecor) continue;
- final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
+ final float transformPos = (float) (child.getLeft() - scrollX) / getPaddedWidth();
mPageTransformer.transformPage(child, transformPos);
}
}
@@ -1785,11 +1831,11 @@
// are dragging.
if (action != MotionEvent.ACTION_DOWN) {
if (mIsBeingDragged) {
- if (DEBUG) Log.v(TAG, "Intercept returning true!");
+ if (DEBUG) Log.v(TAG, "Being dragged, intercept returning true!");
return true;
}
if (mIsUnableToDrag) {
- if (DEBUG) Log.v(TAG, "Intercept returning false!");
+ if (DEBUG) Log.v(TAG, "Unable to drag, intercept returning false!");
return false;
}
}
@@ -1903,13 +1949,6 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (mFakeDragging) {
- // A fake drag is in progress already, ignore this real one
- // but still eat the touch events.
- // (It is likely that the user is multi-touching the screen.)
- return true;
- }
-
if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
// Don't handle edge touches immediately -- they may actually belong to one of our
// descendants.
@@ -1978,19 +2017,26 @@
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+ final int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+
mPopulatePending = true;
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- final ItemInfo ii = infoForCurrentScrollPosition();
+
+ final float scrollStart = getScrollStart();
+ final float scrolledPages = scrollStart / getPaddedWidth();
+ final ItemInfo ii = infoForFirstVisiblePage();
final int currentPage = ii.position;
- final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
- final int activePointerIndex =
- ev.findPointerIndex(mActivePointerId);
+ final float nextPageOffset;
+ if (isLayoutRtl()) {
+ nextPageOffset = (ii.offset - scrolledPages) / ii.widthFactor;
+ } else {
+ nextPageOffset = (scrolledPages - ii.offset) / ii.widthFactor;
+ }
+
+ final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(activePointerIndex);
final int totalDelta = (int) (x - mInitialMotionX);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
+ final int nextPage = determineTargetPage(
+ currentPage, nextPageOffset, initialVelocity, totalDelta);
setCurrentItemInternal(nextPage, true, true, initialVelocity);
mActivePointerId = INVALID_POINTER;
@@ -2038,48 +2084,79 @@
private boolean performDrag(float x) {
boolean needsInvalidate = false;
+ final int width = getPaddedWidth();
final float deltaX = mLastMotionX - x;
mLastMotionX = x;
- float oldScrollX = getScrollX();
- float scrollX = oldScrollX + deltaX;
- final int width = getClientWidth();
-
- float leftBound = width * mFirstOffset;
- float rightBound = width * mLastOffset;
- boolean leftAbsolute = true;
- boolean rightAbsolute = true;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- leftAbsolute = false;
- leftBound = firstItem.offset * width;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- rightAbsolute = false;
- rightBound = lastItem.offset * width;
+ final EdgeEffect startEdge;
+ final EdgeEffect endEdge;
+ if (isLayoutRtl()) {
+ startEdge = mRightEdge;
+ endEdge = mLeftEdge;
+ } else {
+ startEdge = mLeftEdge;
+ endEdge = mRightEdge;
}
- if (scrollX < leftBound) {
- if (leftAbsolute) {
- float over = leftBound - scrollX;
- mLeftEdge.onPull(Math.abs(over) / width);
+ // Translate scroll to relative coordinates.
+ final float nextScrollX = getScrollX() + deltaX;
+ final float scrollStart;
+ if (isLayoutRtl()) {
+ scrollStart = MAX_SCROLL_X - nextScrollX;
+ } else {
+ scrollStart = nextScrollX;
+ }
+
+ final float startBound;
+ final ItemInfo startItem = mItems.get(0);
+ final boolean startAbsolute = startItem.position == 0;
+ if (startAbsolute) {
+ startBound = startItem.offset * width;
+ } else {
+ startBound = width * mFirstOffset;
+ }
+
+ final float endBound;
+ final ItemInfo endItem = mItems.get(mItems.size() - 1);
+ final boolean endAbsolute = endItem.position == mAdapter.getCount() - 1;
+ if (endAbsolute) {
+ endBound = endItem.offset * width;
+ } else {
+ endBound = width * mLastOffset;
+ }
+
+ final float clampedScrollStart;
+ if (scrollStart < startBound) {
+ if (startAbsolute) {
+ final float over = startBound - scrollStart;
+ startEdge.onPull(Math.abs(over) / width);
needsInvalidate = true;
}
- scrollX = leftBound;
- } else if (scrollX > rightBound) {
- if (rightAbsolute) {
- float over = scrollX - rightBound;
- mRightEdge.onPull(Math.abs(over) / width);
+ clampedScrollStart = startBound;
+ } else if (scrollStart > endBound) {
+ if (endAbsolute) {
+ final float over = scrollStart - endBound;
+ endEdge.onPull(Math.abs(over) / width);
needsInvalidate = true;
}
- scrollX = rightBound;
+ clampedScrollStart = endBound;
+ } else {
+ clampedScrollStart = scrollStart;
}
- // Don't lose the rounded component
- mLastMotionX += scrollX - (int) scrollX;
- scrollTo((int) scrollX, getScrollY());
- pageScrolled((int) scrollX);
+
+ // Translate back to absolute coordinates.
+ final float targetScrollX;
+ if (isLayoutRtl()) {
+ targetScrollX = MAX_SCROLL_X - clampedScrollStart;
+ } else {
+ targetScrollX = clampedScrollStart;
+ }
+
+ // Don't lose the rounded component.
+ mLastMotionX += targetScrollX - (int) targetScrollX;
+
+ scrollTo((int) targetScrollX, getScrollY());
+ pageScrolled((int) targetScrollX);
return needsInvalidate;
}
@@ -2088,19 +2165,23 @@
* @return Info about the page at the current scroll position.
* This can be synthetic for a missing middle page; the 'object' field can be null.
*/
- private ItemInfo infoForCurrentScrollPosition() {
- final int width = getClientWidth();
- final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;
+ private ItemInfo infoForFirstVisiblePage() {
+ final int startOffset = getScrollStart();
+ final int width = getPaddedWidth();
+ final float scrollOffset = width > 0 ? (float) startOffset / width : 0;
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+
int lastPos = -1;
float lastOffset = 0.f;
float lastWidth = 0.f;
boolean first = true;
-
ItemInfo lastItem = null;
- for (int i = 0; i < mItems.size(); i++) {
+
+ final int N = mItems.size();
+ for (int i = 0; i < N; i++) {
ItemInfo ii = mItems.get(i);
- float offset;
+
+ // Seek to position.
if (!first && ii.position != lastPos + 1) {
// Create a synthetic item for a missing page.
ii = mTempItem;
@@ -2109,17 +2190,18 @@
ii.widthFactor = mAdapter.getPageWidth(ii.position);
i--;
}
- offset = ii.offset;
- final float leftBound = offset;
- final float rightBound = offset + ii.widthFactor + marginOffset;
- if (first || scrollOffset >= leftBound) {
- if (scrollOffset < rightBound || i == mItems.size() - 1) {
+ final float offset = ii.offset;
+ final float startBound = offset;
+ if (first || scrollOffset >= startBound) {
+ final float endBound = offset + ii.widthFactor + marginOffset;
+ if (scrollOffset < endBound || i == mItems.size() - 1) {
return ii;
}
} else {
return lastItem;
}
+
first = false;
lastPos = ii.position;
lastOffset = offset;
@@ -2130,13 +2212,28 @@
return lastItem;
}
+ private int getScrollStart() {
+ if (isLayoutRtl()) {
+ return MAX_SCROLL_X - getScrollX();
+ } else {
+ return getScrollX();
+ }
+ }
+
+ /**
+ * @param currentPage the position of the page with the first visible starting edge
+ * @param pageOffset the fraction of the right-hand page that's visible
+ * @param velocity the velocity of the touch event stream
+ * @param deltaX the distance of the touch event stream
+ * @return the position of the target page
+ */
private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
int targetPage;
if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
- targetPage = velocity > 0 ? currentPage : currentPage + 1;
+ targetPage = currentPage - (velocity < 0 ? mLeftIncr : 0);
} else {
final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
- targetPage = (int) (currentPage + pageOffset + truncator);
+ targetPage = (int) (currentPage - mLeftIncr * (pageOffset + truncator));
}
if (mItems.size() > 0) {
@@ -2144,7 +2241,7 @@
final ItemInfo lastItem = mItems.get(mItems.size() - 1);
// Only let the user target pages we have items for
- targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
+ targetPage = MathUtils.constrain(targetPage, firstItem.position, lastItem.position);
}
return targetPage;
@@ -2205,6 +2302,7 @@
int itemIndex = 0;
ItemInfo ii = mItems.get(0);
float offset = ii.offset;
+
final int itemCount = mItems.size();
final int firstPos = ii.position;
final int lastPos = mItems.get(itemCount - 1).position;
@@ -2213,156 +2311,39 @@
ii = mItems.get(++itemIndex);
}
- float drawAt;
+ final float itemOffset;
+ final float widthFactor;
if (pos == ii.position) {
- drawAt = (ii.offset + ii.widthFactor) * width;
- offset = ii.offset + ii.widthFactor + marginOffset;
+ itemOffset = ii.offset;
+ widthFactor = ii.widthFactor;
} else {
- float widthFactor = mAdapter.getPageWidth(pos);
- drawAt = (offset + widthFactor) * width;
- offset += widthFactor + marginOffset;
+ itemOffset = offset;
+ widthFactor = mAdapter.getPageWidth(pos);
}
- if (drawAt + mPageMargin > scrollX) {
- mMarginDrawable.setBounds((int) drawAt, mTopPageBounds,
- (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds);
+ final float left;
+ final float scaledOffset = itemOffset * width;
+ if (isLayoutRtl()) {
+ left = MAX_SCROLL_X - scaledOffset;
+ } else {
+ left = scaledOffset + widthFactor * width;
+ }
+
+ offset = itemOffset + widthFactor + marginOffset;
+
+ if (left + mPageMargin > scrollX) {
+ mMarginDrawable.setBounds((int) left, mTopPageBounds,
+ (int) (left + mPageMargin + 0.5f), mBottomPageBounds);
mMarginDrawable.draw(canvas);
}
- if (drawAt > scrollX + width) {
+ if (left > scrollX + width) {
break; // No more visible, no sense in continuing
}
}
}
}
- /**
- * Start a fake drag of the pager.
- *
- * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager
- * with the touch scrolling of another view, while still letting the ViewPager
- * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
- * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
- * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
- *
- * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag
- * is already in progress, this method will return false.
- *
- * @return true if the fake drag began successfully, false if it could not be started.
- *
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean beginFakeDrag() {
- if (mIsBeingDragged) {
- return false;
- }
- mFakeDragging = true;
- setScrollState(SCROLL_STATE_DRAGGING);
- mInitialMotionX = mLastMotionX = 0;
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- mFakeDragBeginTime = time;
- return true;
- }
-
- /**
- * End a fake drag of the pager.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- */
- public void endFakeDrag() {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
- mPopulatePending = true;
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int currentPage = ii.position;
- final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
- final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
- setCurrentItemInternal(nextPage, true, true, initialVelocity);
- endDrag();
-
- mFakeDragging = false;
- }
-
- /**
- * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
- *
- * @param xOffset Offset in pixels to drag by.
- * @see #beginFakeDrag()
- * @see #endFakeDrag()
- */
- public void fakeDragBy(float xOffset) {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- mLastMotionX += xOffset;
-
- float oldScrollX = getScrollX();
- float scrollX = oldScrollX - xOffset;
- final int width = getClientWidth();
-
- float leftBound = width * mFirstOffset;
- float rightBound = width * mLastOffset;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- leftBound = firstItem.offset * width;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- rightBound = lastItem.offset * width;
- }
-
- if (scrollX < leftBound) {
- scrollX = leftBound;
- } else if (scrollX > rightBound) {
- scrollX = rightBound;
- }
- // Don't lose the rounded component
- mLastMotionX += scrollX - (int) scrollX;
- scrollTo((int) scrollX, getScrollY());
- pageScrolled((int) scrollX);
-
- // Synthesize an event for the VelocityTracker.
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
- mLastMotionX, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- }
-
- /**
- * Returns true if a fake drag is in progress.
- *
- * @return true if currently in a fake drag, false otherwise.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean isFakeDragging() {
- return mFakeDragging;
- }
-
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
@@ -2408,7 +2389,7 @@
return false;
}
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final int scrollX = getScrollX();
if (direction < 0) {
return (scrollX > (int) (width * mFirstOffset));
@@ -2438,12 +2419,11 @@
final int count = group.getChildCount();
// Count backwards - let topmost views consume scroll distance first.
for (int i = count - 1; i >= 0; i--) {
- // TODO: Add versioned support here for transformed views.
- // This will not work for transformed views in Honeycomb+
+ // TODO: Add support for transformed views.
final View child = group.getChildAt(i);
- if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
- y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
- canScroll(child, true, dx, x + scrollX - child.getLeft(),
+ if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
+ && y + scrollY >= child.getTop() && y + scrollY < child.getBottom()
+ && canScroll(child, true, dx, x + scrollX - child.getLeft(),
y + scrollY - child.getTop())) {
return true;
}
@@ -2582,19 +2562,22 @@
}
boolean pageLeft() {
- if (mCurItem > 0) {
- setCurrentItem(mCurItem-1, true);
- return true;
- }
- return false;
+ return setCurrentItemInternal(mCurItem + mLeftIncr, true, false);
}
boolean pageRight() {
- if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) {
- setCurrentItem(mCurItem+1, true);
- return true;
+ return setCurrentItemInternal(mCurItem - mLeftIncr, true, false);
+ }
+
+ @Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ if (layoutDirection == LAYOUT_DIRECTION_LTR) {
+ mLeftIncr = -1;
+ } else {
+ mLeftIncr = 1;
}
- return false;
}
/**
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 23b0d50..d86f71a 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -584,6 +584,13 @@
return frameCount * channelCount * audio_bytes_per_sample(format);
}
+static jboolean android_media_AudioRecord_setInputDevice(
+ JNIEnv *env, jobject thiz, jint device_id) {
+
+// sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+// return lpRecorder->setInputDevice(device_id) == NO_ERROR;
+ return false;
+}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -616,6 +623,7 @@
"()I", (void *)android_media_AudioRecord_get_pos_update_period},
{"native_get_min_buff_size",
"(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
+ {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
};
// field names found in android/media/AudioRecord.java
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 62685a1..f430eb5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1309,6 +1309,14 @@
<permission android:name="android.permission.MANAGE_USERS"
android:protectionLevel="signature|system" />
+ <!-- @hide Allows an application to set the profile owners and the device owner.
+ This permission is not available to third party applications.-->
+ <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signature"
+ android:label="@string/permlab_manageProfileAndDeviceOwners"
+ android:description="@string/permdesc_manageProfileAndDeviceOwners" />
+
<!-- Allows an application to get full detailed information about
recently running tasks, with full fidelity to the real state.
@hide -->
@@ -2097,6 +2105,11 @@
android:protectionLevel="signature|development|appop" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <!-- @hide Allows an application to change the app idle state of an app.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to collect battery statistics -->
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|system|development" />
diff --git a/core/res/res/drawable/ic_chevron_right.xml b/core/res/res/drawable/ic_chevron_end.xml
similarity index 91%
rename from core/res/res/drawable/ic_chevron_right.xml
rename to core/res/res/drawable/ic_chevron_end.xml
index 4e6d8e3..8570d26 100644
--- a/core/res/res/drawable/ic_chevron_right.xml
+++ b/core/res/res/drawable/ic_chevron_end.xml
@@ -18,7 +18,8 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true">
<path
android:fillColor="#FF000000"
android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6,-6z"/>
diff --git a/core/res/res/drawable/ic_chevron_left.xml b/core/res/res/drawable/ic_chevron_start.xml
similarity index 91%
rename from core/res/res/drawable/ic_chevron_left.xml
rename to core/res/res/drawable/ic_chevron_start.xml
index dc24706..d412ce0 100644
--- a/core/res/res/drawable/ic_chevron_left.xml
+++ b/core/res/res/drawable/ic_chevron_start.xml
@@ -18,7 +18,8 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true">
<path
android:fillColor="#FF000000"
android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41,-1.41L10.83 12z"/>
diff --git a/core/res/res/drawable/list_highlight_material.xml b/core/res/res/drawable/list_choice_background_material.xml
similarity index 100%
rename from core/res/res/drawable/list_highlight_material.xml
rename to core/res/res/drawable/list_choice_background_material.xml
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index 8125544..2150341 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -42,7 +42,7 @@
<TextView
android:id="@+id/date_picker_header_date"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
android:gravity="start"
diff --git a/core/res/res/layout/day_picker_content_material.xml b/core/res/res/layout/day_picker_content_material.xml
index 1852bfa..b582d74 100644
--- a/core/res/res/layout/day_picker_content_material.xml
+++ b/core/res/res/layout/day_picker_content_material.xml
@@ -30,7 +30,7 @@
android:layout_height="wrap_content"
android:minWidth="48dp"
android:minHeight="48dp"
- android:src="@drawable/ic_chevron_left"
+ android:src="@drawable/ic_chevron_start"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/date_picker_prev_month_button"
android:visibility="invisible" />
@@ -41,7 +41,7 @@
android:layout_height="wrap_content"
android:minWidth="48dp"
android:minHeight="48dp"
- android:src="@drawable/ic_chevron_right"
+ android:src="@drawable/ic_chevron_end"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/date_picker_next_month_button"
android:visibility="invisible" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 00c771d8..887b0a5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6038,19 +6038,8 @@
<!-- values are colors, which are integers starting with "#". -->
<enum name="colorType" value="3" />
</attr>
- <!-- Defines whether the animation should adjust duration in order to achieve the same
- perceived effects on different devices. -->
- <attr name="durationScaleHint" >
- <!-- Default value for scale hint. When set, duration will not be scaled.-->
- <enum name="noScale" value="0"/>
- <!-- This should be used when the animation's moving distance is proportional to screen,
- as the scaling is based on screen size. -->
- <enum name="screenBased" value="1"/>
- <!-- This is for animations that have a distance defined in dp, which will be the same
- across different devices. In this case, scaling is based on the physical distance
- per dp on the current device. -->
- <enum name="dpBased" value="2"/>
- </attr>
+ <!-- Placeholder for a deleted attribute. This should be removed before M release. -->
+ <attr name="removeBeforeMRelease" format="integer" />
</declare-styleable>
<declare-styleable name="PropertyValuesHolder">
@@ -6099,7 +6088,6 @@
<!-- child animations should be played sequentially, in the same order as the xml. -->
<enum name="sequentially" value="1" />
</attr>
- <attr name="durationScaleHint" />
</declare-styleable>
<!-- ========================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index baccafd..875659d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2664,8 +2664,8 @@
<public type="attr" name="supportsAssistGesture" />
<public type="attr" name="thumbPosition" />
- <!-- Animation -->
- <public type="attr" name="durationScaleHint" />
+ <!-- Placeholder for a removed attribute. Remove this before M release. -->
+ <public type="attr" name="removeBeforeMRelease" />
<public type="attr" name="lockTaskMode" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6f554f08..51c2062 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -699,6 +699,12 @@
discover information about which applications are used on the device.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_manageProfileAndDeviceOwners">Manage profile and device owners</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to set the profile/device owners.
+ [CHAR LIMIT=NONE] -->
+ <string name="permdesc_manageProfileAndDeviceOwners">Allows apps to set the profile owners and the device owner.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_reorderTasks">reorder running apps</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_reorderTasks">Allows the app to move tasks to the
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index db178fa..f81ee8c 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -614,7 +614,7 @@
<style name="Widget.Material.GestureOverlayView" parent="Widget.GestureOverlayView"/>
<style name="Widget.Material.GridView" parent="Widget.GridView">
- <item name="listSelector">?attr/selectableItemBackground</item>
+ <item name="listSelector">?attr/listChoiceBackgroundIndicator</item>
</style>
<style name="Widget.Material.CalendarView" parent="Widget.CalendarView">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e24150..5b13325 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2235,8 +2235,6 @@
<java-symbol type="dimen" name="floating_toolbar_horizontal_margin" />
<java-symbol type="dimen" name="floating_toolbar_vertical_margin" />
- <java-symbol type="drawable" name="ic_chevron_left" />
- <java-symbol type="drawable" name="ic_chevron_right" />
<java-symbol type="string" name="date_picker_prev_month_button" />
<java-symbol type="string" name="date_picker_next_month_button" />
<java-symbol type="layout" name="date_picker_month_item_material" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index a413d91..e8aab07 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -129,8 +129,8 @@
<item name="listChoiceIndicatorSingle">@drawable/btn_radio_material_anim</item>
<item name="listChoiceIndicatorMultiple">@drawable/btn_check_material_anim</item>
- <item name="listChoiceBackgroundIndicator">@drawable/list_highlight_material</item>
- <item name="activatedBackgroundIndicator">@null</item>
+ <item name="listChoiceBackgroundIndicator">@drawable/list_choice_background_material</item>
+ <item name="activatedBackgroundIndicator">@drawable/activated_background_material</item>
<item name="listDividerAlertDialog">@null</item>
@@ -485,7 +485,7 @@
<item name="listChoiceIndicatorSingle">@drawable/btn_radio_material_anim</item>
<item name="listChoiceIndicatorMultiple">@drawable/btn_check_material_anim</item>
- <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
+ <item name="listChoiceBackgroundIndicator">@drawable/list_choice_background_material</item>
<item name="activatedBackgroundIndicator">@drawable/activated_background_material</item>
<item name="expandableListPreferredItemPaddingLeft">40dip</item>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 279bfbf..baa772e 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -1587,91 +1587,13 @@
}
}
- private class PackageMoveObserver extends IPackageMoveObserver.Stub {
- public int returnCode;
-
- private boolean doneFlag = false;
-
- public String packageName;
-
- public PackageMoveObserver(String pkgName) {
- packageName = pkgName;
- }
-
- public void packageMoved(String packageName, int returnCode) {
- Log.i("DEBUG_MOVE::", "pkg = " + packageName + ", " + "ret = " + returnCode);
- if (!packageName.equals(this.packageName)) {
- return;
- }
- synchronized (this) {
- this.returnCode = returnCode;
- doneFlag = true;
- notifyAll();
- }
- }
-
- public boolean isDone() {
- return doneFlag;
- }
- }
-
public boolean invokeMovePackage(String pkgName, int flags, GenericReceiver receiver)
throws Exception {
- PackageMoveObserver observer = new PackageMoveObserver(pkgName);
- final boolean received = false;
- mContext.registerReceiver(receiver, receiver.filter);
- try {
- // Wait on observer
- synchronized (observer) {
- synchronized (receiver) {
- getPm().movePackage(pkgName, observer, flags);
- long waitTime = 0;
- while ((!observer.isDone()) && (waitTime < MAX_WAIT_TIME)) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if (!observer.isDone()) {
- throw new Exception("Timed out waiting for pkgmove callback");
- }
- if (observer.returnCode != PackageManager.MOVE_SUCCEEDED) {
- return false;
- }
- // Verify we received the broadcast
- waitTime = 0;
- while ((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME)) {
- receiver.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if (!receiver.isDone()) {
- throw new Exception("Timed out waiting for MOVE notifications");
- }
- return receiver.received;
- }
- }
- } finally {
- mContext.unregisterReceiver(receiver);
- }
+ throw new UnsupportedOperationException();
}
private boolean invokeMovePackageFail(String pkgName, int flags, int errCode) throws Exception {
- PackageMoveObserver observer = new PackageMoveObserver(pkgName);
- try {
- // Wait on observer
- synchronized (observer) {
- getPm().movePackage(pkgName, observer, flags);
- long waitTime = 0;
- while ((!observer.isDone()) && (waitTime < MAX_WAIT_TIME)) {
- observer.wait(WAIT_TIME_INCR);
- waitTime += WAIT_TIME_INCR;
- }
- if (!observer.isDone()) {
- throw new Exception("Timed out waiting for pkgmove callback");
- }
- assertEquals(errCode, observer.returnCode);
- }
- } finally {
- }
- return true;
+ throw new UnsupportedOperationException();
}
private int getDefaultInstallLoc() {
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 5c9e813..ee9e2e4 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,6 +21,7 @@
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import android.content.Context;
+import android.provider.DocumentsContract.Document;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
@@ -43,6 +44,8 @@
private File mDir;
private File mTestFile;
private File mCopyFile;
+ private File mTarget;
+
@Override
protected void setUp() throws Exception {
@@ -50,11 +53,15 @@
mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
mTestFile = new File(mDir, "test.file");
mCopyFile = new File(mDir, "copy.file");
+
+ mTarget = getContext().getFilesDir();
+ FileUtils.deleteContents(mTarget);
}
@Override
protected void tearDown() throws Exception {
IoUtils.deleteContents(mDir);
+ FileUtils.deleteContents(mTarget);
}
// TODO: test setPermissions(), getPermissions()
@@ -225,6 +232,63 @@
assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
}
+ public void testBuildUniqueFile_normal() throws Exception {
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ assertNameEquals("test.jpeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
+ assertNameEquals("TEst.JPeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
+ assertNameEquals("test.png.jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
+ assertNameEquals("test.png.jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png"));
+
+ assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test"));
+ assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test.flac"));
+ assertNameEquals("test.flac",
+ FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test"));
+ assertNameEquals("test.flac",
+ FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
+ }
+
+ public void testBuildUniqueFile_unknown() throws Exception {
+ assertNameEquals("test",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
+ assertNameEquals("test.jpg",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
+ assertNameEquals(".test",
+ FileUtils.buildUniqueFile(mTarget, "application/octet-stream", ".test"));
+
+ assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test"));
+ assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
+ }
+
+ public void testBuildUniqueFile_dir() throws Exception {
+ assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+ new File(mTarget, "test").mkdir();
+ assertNameEquals("test (1)",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+
+ assertNameEquals("test.jpg",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+ new File(mTarget, "test.jpg").mkdir();
+ assertNameEquals("test.jpg (1)",
+ FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+ }
+
+ public void testBuildUniqueFile_increment() throws Exception {
+ assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ new File(mTarget, "test.jpg").createNewFile();
+ assertNameEquals("test (1).jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ new File(mTarget, "test (1).jpg").createNewFile();
+ assertNameEquals("test (2).jpg",
+ FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ }
+
+ private static void assertNameEquals(String expected, File actual) {
+ assertEquals(expected, actual.getName());
+ }
+
private void touch(String name, long age) throws Exception {
final File file = new File(mDir, name);
file.createNewFile();
diff --git a/docs/html/google/play-services/index.jd b/docs/html/google/play-services/index.jd
index d674f7f..5ccdcb9 100644
--- a/docs/html/google/play-services/index.jd
+++ b/docs/html/google/play-services/index.jd
@@ -74,30 +74,8 @@
<a href="http://android-developers.blogspot.com/2015/04/theres-lot-to-explore-with-google-play.html"
class="external-link">blog post</a>.</p>
<ul>
- <li><strong>Maps</strong> - This release makes the Google Maps Android API v2 available on
-<a href="https://developers.google.com/maps/documentation/android/wear" class="external-link">
-Android Wear</a>, so you can now create map-based apps that run directly on wearable devices. In
-addition, the Maps API now offers a new
-<a href="{@docRoot}reference/com/google/android/gms/maps/StreetViewPanorama.OnStreetViewPanoramaLongClickListener.html">
-{@code OnStreetViewPanoramaLongClickListener}</a> interface, similar to the existing
-<a href="{@docRoot}reference/com/google/android/gms/maps/GoogleMap.OnMapLongClickListener.html">
-{@code OnMapLongClickListener}</a> interface. These listeners are particularly helpful
-for wearable devices, so you can let users exit from the app by long-clicking on a map or panorama.
-On a wearable device, the swipe gesture is used to pan the map instead of exiting the app.
- <ul>
- <li><a href="https://developers.google.com/maps/documentation/android/wear"
- class="external-link">Google Maps on Android Wear developer guide</a>
- </li>
- <li><a href="https://github.com/googlemaps/android-samples/tree/master/BasicWearMap"
- class="external-link">Google Maps on Android Wear sample</a>
- </li>
- <li><a href="https://developers.google.com/maps/documentation/android/releases"
- class="external-link">Release notes</a>
- </li>
- </ul>
- </li>
<li>
- <strong>Wear</strong> - In addition to Maps support, this release provides you with the ability
+ <strong>Wear</strong> - This release provides you with the ability
to advertise and discover the capabilities of devices that are connected in a Wear network, through
the new <a href="{@docRoot}reference/com/google/android/gms/wearable/CapabilityApi.html">
{@code CapabilityApi}</a> class. The new
diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index 19f510a..2a908ba 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -71,7 +71,7 @@
</tr>
<tr>
-<td rowspan="11">Audio</td>
+<td rowspan="12">Audio</td>
<td>AAC LC</td>
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
@@ -180,6 +180,15 @@
</tr>
<tr>
+<td>Opus</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"><big>•</big><br><small>(Android 5.0+)</small></td>
+<td></td>
+<td>
+ Matroska (.mkv)</td>
+</tr>
+
+<tr>
<td rowspan="5">Image</td>
<td>JPEG</td>
<td style="text-align: center;"><big>•</big></td>
@@ -235,7 +244,7 @@
<tr>
-<td rowspan="4">Video</td>
+<td rowspan="6">Video</td>
<td>H.263</td>
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
@@ -257,6 +266,15 @@
</tr>
<tr>
+<td>H.265 HEVC</td>
+<td style="text-align: center;" nowrap></td>
+<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 5.0+)</small></td>
+<td>Main Profile Level 3 for mobile devices and Main Profile Level 4.1 for Android TV</td>
+<td>
+ • MPEG-4 (.mp4)<br>
+</tr>
+
+<tr>
<td>MPEG-4 SP</td>
<td> </td>
<td style="text-align: center;"><big>•</big></td>
@@ -275,6 +293,16 @@
• Matroska (.mkv, Android 4.0+)</td>
</tr>
+<tr>
+<td>VP9</td>
+<td style="text-align: center;" nowrap></td>
+<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 4.4+)</small></td>
+<td></td>
+<td>
+ • <a href="http://www.webmproject.org/">WebM</a> (.webm)<br>
+ • Matroska (.mkv, Android 4.0+)</td>
+</tr>
+
</tbody></table>
diff --git a/docs/html/training/tv/start/start.jd b/docs/html/training/tv/start/start.jd
index 2766e90..0f5871f 100644
--- a/docs/html/training/tv/start/start.jd
+++ b/docs/html/training/tv/start/start.jd
@@ -1,4 +1,4 @@
-page.title=Get Started with TV Apps
+page.title=Getting Started with TV Apps
page.tags="leanback","recyclerview","launcher"
trainingnavtop=true
@@ -10,6 +10,7 @@
<div id="tb">
<h2>This lesson teaches you how to</h2>
<ol>
+ <li><a href="#media">Determine Media Format Support</a></li>
<li><a href="#dev-project">Setup a TV Project</a></li>
<li><a href="#tv-libraries">Add TV Support Libraries</a></li>
<li><a href="#build-it">Build TV Apps</a></li>
@@ -42,6 +43,18 @@
minimum required changes to enable an app to run on TV devices.
</p>
+<h2 id="media">Determine Media Format Support</h2>
+
+<p>See the following documentation for information about the codecs, protocols, and formats
+supported by Android TV.</p>
+
+<ul>
+ <li><a href="{@docRoot}guide/appendix/media-formats.html">Supported Media Formats</a></li>
+ <li><a class="external-link" href="https://source.android.com/devices/drm.html">DRM</a></li>
+ <li><code><a href="{@docRoot}reference/android/drm/package-summary.html">android.drm</a></code></li>
+ <li><a href="{@docRoot}guide/topics/media/exoplayer.html">ExoPlayer</a></li>
+ <li>{@link android.media.MediaPlayer android.media.MediaPlayer}</li>
+</ul>
<h2 id="dev-project">Set up a TV Project</h2>
@@ -284,9 +297,15 @@
TV devices.
</li>
<li>
- <a href="{@docRoot}training/tv/games/index.html">Games for TV</a> - TV devices are a great
+ <a href="{@docRoot}training/tv/games/index.html">Building TV Games</a> - TV devices are a great
platform for games. See this topic for information on building great game experiences for TV.
</li>
+ <li>
+ <a href="{@docRoot}training/tv/tif/index.html">Building Live TV Apps</a> - Present your video
+ content in a linear, "broadcast TV" style with channels and programs that your users can access
+ through a program guide as well as the channel up/down buttons.
+ </li>
+
</ul>
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index c63c8ba..d6f8cca 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -181,7 +181,7 @@
public static final int JPEG = 0x100;
/**
- * <p>Multi-plane Android YUV format</p>
+ * <p>Multi-plane Android YUV 420 format</p>
*
* <p>This format is a generic YCbCr format, capable of describing any 4:2:0
* chroma-subsampled planar or semiplanar buffer (but not fully interleaved),
@@ -219,6 +219,135 @@
public static final int YUV_420_888 = 0x23;
/**
+ * <p>Multi-plane Android YUV 422 format</p>
+ *
+ * <p>This format is a generic YCbCr format, capable of describing any 4:2:2
+ * chroma-subsampled (planar, semiplanar or interleaved) format,
+ * with 8 bits per color sample.</p>
+ *
+ * <p>Images in this format are always represented by three separate buffers
+ * of data, one for each color plane. Additional information always
+ * accompanies the buffers, describing the row stride and the pixel stride
+ * for each plane.</p>
+ *
+ * <p>The order of planes in the array returned by
+ * {@link android.media.Image#getPlanes() Image#getPlanes()} is guaranteed such that
+ * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V (Cr).</p>
+ *
+ * <p>In contrast to the {@link #YUV_420_888} format, the Y-plane may have a pixel
+ * stride greater than 1 in
+ * {@link android.media.Image.Plane#getPixelStride() yPlane.getPixelStride()}.</p>
+ *
+ * <p>The U/V planes are guaranteed to have the same row stride and pixel stride
+ * (in particular,
+ * {@link android.media.Image.Plane#getRowStride() uPlane.getRowStride()}
+ * == {@link android.media.Image.Plane#getRowStride() vPlane.getRowStride()} and
+ * {@link android.media.Image.Plane#getPixelStride() uPlane.getPixelStride()}
+ * == {@link android.media.Image.Plane#getPixelStride() vPlane.getPixelStride()};
+ * ).</p>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.media.MediaCodec}
+ * through {@link android.media.MediaCodec#getOutputImage} object.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.MediaCodec
+ */
+ public static final int YUV_422_888 = 0x27;
+
+ /**
+ * <p>Multi-plane Android YUV 444 format</p>
+ *
+ * <p>This format is a generic YCbCr format, capable of describing any 4:4:4
+ * (planar, semiplanar or interleaved) format,
+ * with 8 bits per color sample.</p>
+ *
+ * <p>Images in this format are always represented by three separate buffers
+ * of data, one for each color plane. Additional information always
+ * accompanies the buffers, describing the row stride and the pixel stride
+ * for each plane.</p>
+ *
+ * <p>The order of planes in the array returned by
+ * {@link android.media.Image#getPlanes() Image#getPlanes()} is guaranteed such that
+ * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V (Cr).</p>
+ *
+ * <p>In contrast to the {@link #YUV_420_888} format, the Y-plane may have a pixel
+ * stride greater than 1 in
+ * {@link android.media.Image.Plane#getPixelStride() yPlane.getPixelStride()}.</p>
+ *
+ * <p>The U/V planes are guaranteed to have the same row stride and pixel stride
+ * (in particular,
+ * {@link android.media.Image.Plane#getRowStride() uPlane.getRowStride()}
+ * == {@link android.media.Image.Plane#getRowStride() vPlane.getRowStride()} and
+ * {@link android.media.Image.Plane#getPixelStride() uPlane.getPixelStride()}
+ * == {@link android.media.Image.Plane#getPixelStride() vPlane.getPixelStride()};
+ * ).</p>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.media.MediaCodec}
+ * through {@link android.media.MediaCodec#getOutputImage} object.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.MediaCodec
+ */
+ public static final int YUV_444_888 = 0x28;
+
+ /**
+ * <p>Multi-plane Android RGB format</p>
+ *
+ * <p>This format is a generic RGB format, capable of describing most RGB formats,
+ * with 8 bits per color sample.</p>
+ *
+ * <p>Images in this format are always represented by three separate buffers
+ * of data, one for each color plane. Additional information always
+ * accompanies the buffers, describing the row stride and the pixel stride
+ * for each plane.</p>
+ *
+ * <p>The order of planes in the array returned by
+ * {@link android.media.Image#getPlanes() Image#getPlanes()} is guaranteed such that
+ * plane #0 is always R (red), plane #1 is always G (green), and plane #2 is always B
+ * (blue).</p>
+ *
+ * <p>All three planes are guaranteed to have the same row strides and pixel strides.</p>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.media.MediaCodec}
+ * through {@link android.media.MediaCodec#getOutputImage} object.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.MediaCodec
+ */
+ public static final int FLEX_RGB_888 = 0x29;
+
+ /**
+ * <p>Multi-plane Android RGBA format</p>
+ *
+ * <p>This format is a generic RGBA format, capable of describing most RGBA formats,
+ * with 8 bits per color sample.</p>
+ *
+ * <p>Images in this format are always represented by four separate buffers
+ * of data, one for each color plane. Additional information always
+ * accompanies the buffers, describing the row stride and the pixel stride
+ * for each plane.</p>
+ *
+ * <p>The order of planes in the array returned by
+ * {@link android.media.Image#getPlanes() Image#getPlanes()} is guaranteed such that
+ * plane #0 is always R (red), plane #1 is always G (green), plane #2 is always B (blue),
+ * and plane #3 is always A (alpha). This format may represent pre-multiplied or
+ * non-premultiplied alpha.</p>
+ *
+ * <p>All four planes are guaranteed to have the same row strides and pixel strides.</p>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.media.MediaCodec}
+ * through {@link android.media.MediaCodec#getOutputImage} object.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.MediaCodec
+ */
+ public static final int FLEX_RGBA_8888 = 0x2A;
+
+ /**
* <p>General raw camera sensor image format, usually representing a
* single-channel Bayer-mosaic image. Each pixel color sample is stored with
* 16 bits of precision.</p>
@@ -543,6 +672,14 @@
return 12;
case YUV_420_888:
return 12;
+ case YUV_422_888:
+ return 16;
+ case YUV_444_888:
+ return 24;
+ case FLEX_RGB_888:
+ return 24;
+ case FLEX_RGBA_8888:
+ return 32;
case RAW_SENSOR:
return 16;
case RAW10:
@@ -574,6 +711,10 @@
case JPEG:
case NV21:
case YUV_420_888:
+ case YUV_422_888:
+ case YUV_444_888:
+ case FLEX_RGB_888:
+ case FLEX_RGBA_8888:
case RAW_SENSOR:
case RAW10:
case RAW12:
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 5fae831..3b25ba6 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -17,7 +17,7 @@
package android.security;
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
-import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.NativeConstants;
import com.android.org.conscrypt.OpenSSLEngine;
import java.security.InvalidAlgorithmParameterException;
@@ -206,9 +206,9 @@
}
private static int getDefaultKeySize(int keyType) {
- if (keyType == NativeCrypto.EVP_PKEY_EC) {
+ if (keyType == NativeConstants.EVP_PKEY_EC) {
return EC_DEFAULT_KEY_SIZE;
- } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
+ } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
return RSA_DEFAULT_KEY_SIZE;
}
return -1;
@@ -216,12 +216,12 @@
private static void checkValidKeySize(String keyAlgorithm, int keyType, int keySize)
throws InvalidAlgorithmParameterException {
- if (keyType == NativeCrypto.EVP_PKEY_EC) {
+ if (keyType == NativeConstants.EVP_PKEY_EC) {
if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) {
throw new InvalidAlgorithmParameterException("EC keys must be >= "
+ EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE);
}
- } else if (keyType == NativeCrypto.EVP_PKEY_RSA) {
+ } else if (keyType == NativeConstants.EVP_PKEY_RSA) {
if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
throw new InvalidAlgorithmParameterException("RSA keys must be >= "
+ RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
@@ -234,7 +234,7 @@
private static void checkCorrectParametersSpec(int keyType, int keySize,
AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
- if (keyType == NativeCrypto.EVP_PKEY_RSA && spec != null) {
+ if (keyType == NativeConstants.EVP_PKEY_RSA && spec != null) {
if (spec instanceof RSAKeyGenParameterSpec) {
RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
if (keySize != -1 && keySize != rsaSpec.getKeysize()) {
@@ -260,7 +260,7 @@
private static byte[][] getArgsForKeyType(int keyType, AlgorithmParameterSpec spec) {
switch (keyType) {
- case NativeCrypto.EVP_PKEY_RSA:
+ case NativeConstants.EVP_PKEY_RSA:
if (spec instanceof RSAKeyGenParameterSpec) {
RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) spec;
return new byte[][] { rsaSpec.getPublicExponent().toByteArray() };
diff --git a/keystore/java/android/security/CryptoOperationException.java b/keystore/java/android/security/CryptoOperationException.java
deleted file mode 100644
index 1c9d005..0000000
--- a/keystore/java/android/security/CryptoOperationException.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security;
-
-/**
- * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
- * exception.
- *
- * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
- * {@code Signature}) is that they can throw a checked exception during initialization, but are not
- * permitted to throw a checked exception during operation. Because crypto operations can fail
- * for a variety of reasons after initialization, this base class provides type-safety for unchecked
- * exceptions that may be thrown in those cases.
- */
-public class CryptoOperationException extends RuntimeException {
-
- /**
- * Constructs a new {@code CryptoOperationException} without detail message and cause.
- */
- public CryptoOperationException() {
- super();
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided detail message and no
- * cause.
- */
- public CryptoOperationException(String message) {
- super(message);
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
- */
- public CryptoOperationException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /**
- * Constructs a new {@code CryptoOperationException} with the provided cause.
- */
- public CryptoOperationException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/keystore/java/android/security/KeyExpiredException.java b/keystore/java/android/security/KeyExpiredException.java
index a02dc33..f58e48a 100644
--- a/keystore/java/android/security/KeyExpiredException.java
+++ b/keystore/java/android/security/KeyExpiredException.java
@@ -16,11 +16,13 @@
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation failed because the employed key's validity end date
* is in the past.
*/
-public class KeyExpiredException extends CryptoOperationException {
+public class KeyExpiredException extends InvalidKeyException {
/**
* Constructs a new {@code KeyExpiredException} without detail message and cause.
diff --git a/keystore/java/android/security/KeyNotYetValidException.java b/keystore/java/android/security/KeyNotYetValidException.java
index 964cd7e..4ea27ef 100644
--- a/keystore/java/android/security/KeyNotYetValidException.java
+++ b/keystore/java/android/security/KeyNotYetValidException.java
@@ -16,11 +16,13 @@
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation failed because the employed key's validity start date
* is in the future.
*/
-public class KeyNotYetValidException extends CryptoOperationException {
+public class KeyNotYetValidException extends InvalidKeyException {
/**
* Constructs a new {@code KeyNotYetValidException} without detail message and cause.
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 5af0527..8c49ff0 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -16,7 +16,7 @@
package android.security;
-import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.NativeConstants;
import android.os.Binder;
import android.os.IBinder;
@@ -30,6 +30,7 @@
import android.security.keymaster.OperationResult;
import android.util.Log;
+import java.security.InvalidKeyException;
import java.util.Locale;
/**
@@ -87,9 +88,9 @@
static int getKeyTypeForAlgorithm(String keyType) {
if ("RSA".equalsIgnoreCase(keyType)) {
- return NativeCrypto.EVP_PKEY_RSA;
+ return NativeConstants.EVP_PKEY_RSA;
} else if ("EC".equalsIgnoreCase(keyType)) {
- return NativeCrypto.EVP_PKEY_EC;
+ return NativeConstants.EVP_PKEY_EC;
} else {
return -1;
}
@@ -508,7 +509,11 @@
}
}
- public static KeyStoreException getKeyStoreException(int errorCode) {
+ /**
+ * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
+ * code.
+ */
+ static KeyStoreException getKeyStoreException(int errorCode) {
if (errorCode > 0) {
// KeyStore layer error
switch (errorCode) {
@@ -544,7 +549,11 @@
}
}
- public static CryptoOperationException getCryptoOperationException(KeyStoreException e) {
+ /**
+ * Returns an {@link InvalidKeyException} corresponding to the provided
+ * {@link KeyStoreException}.
+ */
+ static InvalidKeyException getInvalidKeyException(KeyStoreException e) {
switch (e.getErrorCode()) {
case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
return new KeyExpiredException();
@@ -556,11 +565,15 @@
// case KeymasterDefs.KM_ERROR_TBD
// return new NewFingerprintEnrolledException();
default:
- return new CryptoOperationException("Crypto operation failed", e);
+ return new InvalidKeyException("Keystore operation failed", e);
}
}
- public static CryptoOperationException getCryptoOperationException(int errorCode) {
- return getCryptoOperationException(getKeyStoreException(errorCode));
+ /**
+ * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
+ * code.
+ */
+ static InvalidKeyException getInvalidKeyException(int errorCode) {
+ return getInvalidKeyException(getKeyStoreException(errorCode));
}
}
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 37e00b2..3b13e83 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -136,6 +136,14 @@
private Long mOperationHandle;
private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+ /**
+ * Encountered exception which could not be immediately thrown because it was encountered inside
+ * a method that does not throw checked exception. This exception will be thrown from
+ * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and
+ * {@code engineDoFinal} start ignoring input data.
+ */
+ private Exception mCachedException;
+
protected KeyStoreCipherSpi(
int keymasterAlgorithm,
int keymasterBlockMode,
@@ -152,29 +160,62 @@
@Override
protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
- init(opmode, key, random);
- initAlgorithmSpecificParameters();
- ensureKeystoreOperationInitialized();
+ resetAll();
+
+ boolean success = false;
+ try {
+ init(opmode, key, random);
+ initAlgorithmSpecificParameters();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new InvalidKeyException(e);
+ }
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
}
@Override
protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
- init(opmode, key, random);
- initAlgorithmSpecificParameters(params);
- ensureKeystoreOperationInitialized();
+ resetAll();
+
+ boolean success = false;
+ try {
+ init(opmode, key, random);
+ initAlgorithmSpecificParameters(params);
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
}
@Override
protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
- init(opmode, key, random);
- initAlgorithmSpecificParameters(params);
- ensureKeystoreOperationInitialized();
+ resetAll();
+
+ boolean success = false;
+ try {
+ init(opmode, key, random);
+ initAlgorithmSpecificParameters(params);
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
}
private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
- resetAll();
if (!(key instanceof KeyStoreSecretKey)) {
throw new InvalidKeyException(
"Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
@@ -207,6 +248,7 @@
mOperationToken = null;
mOperationHandle = null;
mMainDataStreamer = null;
+ mCachedException = null;
}
private void resetWhilePreservingInitState() {
@@ -218,12 +260,17 @@
mOperationHandle = null;
mMainDataStreamer = null;
mAdditionalEntropyForBegin = null;
+ mCachedException = null;
}
- private void ensureKeystoreOperationInitialized() {
+ private void ensureKeystoreOperationInitialized() throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
if (mMainDataStreamer != null) {
return;
}
+ if (mCachedException != null) {
+ return;
+ }
if (mKey == null) {
throw new IllegalStateException("Not initialized");
}
@@ -252,11 +299,15 @@
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(opResult.resultCode);
+ switch (opResult.resultCode) {
+ case KeymasterDefs.KM_ERROR_INVALID_NONCE:
+ throw new InvalidAlgorithmParameterException("Invalid IV");
+ }
+ throw KeyStore.getInvalidKeyException(opResult.resultCode);
}
if (opResult.token == null) {
- throw new CryptoOperationException("Keystore returned null operation token");
+ throw new IllegalStateException("Keystore returned null operation token");
}
mOperationToken = opResult.token;
mOperationHandle = opResult.operationHandle;
@@ -270,7 +321,15 @@
@Override
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
- ensureKeystoreOperationInitialized();
+ if (mCachedException != null) {
+ return null;
+ }
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ mCachedException = e;
+ return null;
+ }
if (inputLen == 0) {
return null;
@@ -280,7 +339,8 @@
try {
output = mMainDataStreamer.update(input, inputOffset, inputLen);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ mCachedException = e;
+ return null;
}
if (output.length == 0) {
@@ -309,7 +369,16 @@
@Override
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
- ensureKeystoreOperationInitialized();
+ if (mCachedException != null) {
+ throw (IllegalBlockSizeException)
+ new IllegalBlockSizeException().initCause(mCachedException);
+ }
+
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+ }
byte[] output;
try {
@@ -323,7 +392,7 @@
case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
throw new AEADBadTagException();
default:
- throw KeyStore.getCryptoOperationException(e);
+ throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
}
}
@@ -584,11 +653,11 @@
if (mIv == null) {
mIv = returnedIv;
} else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
- throw new CryptoOperationException("IV in use differs from provided IV");
+ throw new IllegalStateException("IV in use differs from provided IV");
}
} else {
if (returnedIv != null) {
- throw new CryptoOperationException(
+ throw new IllegalStateException(
"IV in use despite IV not being used by this transformation");
}
}
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
index 8ed6e04..1aa3aec 100644
--- a/keystore/java/android/security/KeyStoreConnectException.java
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -21,7 +21,7 @@
*
* @hide
*/
-public class KeyStoreConnectException extends CryptoOperationException {
+public class KeyStoreConnectException extends IllegalStateException {
public KeyStoreConnectException() {
super("Failed to communicate with keystore service");
}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
index aafd2fa..0619199 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -136,7 +136,7 @@
// More input is available, but it wasn't included into the previous chunk
// because the chunk reached its maximum permitted size.
// Shouldn't have happened.
- throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
+ throw new IllegalStateException("Nothing consumed from max-sized chunk: "
+ chunk.length + " bytes");
}
mBuffered = chunk;
@@ -148,7 +148,7 @@
mBufferedOffset = opResult.inputConsumed;
mBufferedLength = chunk.length - opResult.inputConsumed;
} else {
- throw new CryptoOperationException("Consumed more than provided: "
+ throw new IllegalStateException("Consumed more than provided: "
+ opResult.inputConsumed + ", provided: " + chunk.length);
}
@@ -160,7 +160,7 @@
try {
bufferedOutput.write(opResult.output);
} catch (IOException e) {
- throw new CryptoOperationException("Failed to buffer output", e);
+ throw new IllegalStateException("Failed to buffer output", e);
}
}
} else {
@@ -173,7 +173,7 @@
try {
bufferedOutput.write(opResult.output);
} catch (IOException e) {
- throw new CryptoOperationException("Failed to buffer output", e);
+ throw new IllegalStateException("Failed to buffer output", e);
}
return bufferedOutput.toByteArray();
}
@@ -233,10 +233,10 @@
}
if (opResult.inputConsumed < chunk.length) {
- throw new CryptoOperationException("Keystore failed to consume all input. Provided: "
+ throw new IllegalStateException("Keystore failed to consume all input. Provided: "
+ chunk.length + ", consumed: " + opResult.inputConsumed);
} else if (opResult.inputConsumed > chunk.length) {
- throw new CryptoOperationException("Keystore consumed more input than provided"
+ throw new IllegalStateException("Keystore consumed more input than provided"
+ " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
}
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index a19bbda..175369c 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -69,9 +69,10 @@
private final int mKeymasterDigest;
private final int mMacSizeBytes;
- private String mKeyAliasInKeyStore;
+ // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+ private KeyStoreSecretKey mKey;
- // The fields below are reset by the engineReset operation.
+ // Fields below are reset when engineDoFinal succeeds.
private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
private IBinder mOperationToken;
private Long mOperationHandle;
@@ -89,28 +90,39 @@
@Override
protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
InvalidAlgorithmParameterException {
+ resetAll();
+
+ boolean success = false;
+ try {
+ init(key, params);
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ private void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
if (key == null) {
throw new InvalidKeyException("key == null");
} else if (!(key instanceof KeyStoreSecretKey)) {
throw new InvalidKeyException(
"Only Android KeyStore secret keys supported. Key: " + key);
}
+ mKey = (KeyStoreSecretKey) key;
if (params != null) {
throw new InvalidAlgorithmParameterException(
"Unsupported algorithm parameters: " + params);
}
- mKeyAliasInKeyStore = ((KeyStoreSecretKey) key).getAlias();
- if (mKeyAliasInKeyStore == null) {
- throw new InvalidKeyException("Key's KeyStore alias not known");
- }
- engineReset();
- ensureKeystoreOperationInitialized();
}
- @Override
- protected void engineReset() {
+ private void resetAll() {
+ mKey = null;
IBinder operationToken = mOperationToken;
if (operationToken != null) {
mOperationToken = null;
@@ -120,11 +132,26 @@
mChunkedStreamer = null;
}
- private void ensureKeystoreOperationInitialized() {
+ private void resetWhilePreservingInitState() {
+ IBinder operationToken = mOperationToken;
+ if (operationToken != null) {
+ mOperationToken = null;
+ mKeyStore.abort(operationToken);
+ }
+ mOperationHandle = null;
+ mChunkedStreamer = null;
+ }
+
+ @Override
+ protected void engineReset() {
+ resetWhilePreservingInitState();
+ }
+
+ private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
if (mChunkedStreamer != null) {
return;
}
- if (mKeyAliasInKeyStore == null) {
+ if (mKey == null) {
throw new IllegalStateException("Not initialized");
}
@@ -132,7 +159,8 @@
keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore,
+ OperationResult opResult = mKeyStore.begin(
+ mKey.getAlias(),
KeymasterDefs.KM_PURPOSE_SIGN,
true,
keymasterArgs,
@@ -141,10 +169,10 @@
if (opResult == null) {
throw new KeyStoreConnectException();
} else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(opResult.resultCode);
+ throw KeyStore.getInvalidKeyException(opResult.resultCode);
}
if (opResult.token == null) {
- throw new CryptoOperationException("Keystore returned null operation token");
+ throw new IllegalStateException("Keystore returned null operation token");
}
mOperationToken = opResult.token;
mOperationHandle = opResult.operationHandle;
@@ -160,31 +188,39 @@
@Override
protected void engineUpdate(byte[] input, int offset, int len) {
- ensureKeystoreOperationInitialized();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Failed to reinitialize MAC", e);
+ }
byte[] output;
try {
output = mChunkedStreamer.update(input, offset, len);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ throw new IllegalStateException("Keystore operation failed", e);
}
if ((output != null) && (output.length != 0)) {
- throw new CryptoOperationException("Update operation unexpectedly produced output");
+ throw new IllegalStateException("Update operation unexpectedly produced output");
}
}
@Override
protected byte[] engineDoFinal() {
- ensureKeystoreOperationInitialized();
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Failed to reinitialize MAC", e);
+ }
byte[] result;
try {
result = mChunkedStreamer.doFinal(null, 0, 0);
} catch (KeyStoreException e) {
- throw KeyStore.getCryptoOperationException(e);
+ throw new IllegalStateException("Keystore operation failed", e);
}
- engineReset();
+ resetWhilePreservingInitState();
return result;
}
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index d1abe12..293c4c9 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -210,7 +210,8 @@
int errorCode = mKeyStore.generateKey(
keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
if (errorCode != KeyStore.NO_ERROR) {
- throw KeyStore.getCryptoOperationException(errorCode);
+ throw new IllegalStateException(
+ "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
}
String keyAlgorithmJCA =
KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest);
diff --git a/keystore/java/android/security/NewFingerprintEnrolledException.java b/keystore/java/android/security/NewFingerprintEnrolledException.java
index 806b214..4fe210b 100644
--- a/keystore/java/android/security/NewFingerprintEnrolledException.java
+++ b/keystore/java/android/security/NewFingerprintEnrolledException.java
@@ -16,11 +16,13 @@
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation could not be performed because the key used by the
* operation is permanently invalid because a new fingerprint was enrolled.
*/
-public class NewFingerprintEnrolledException extends CryptoOperationException {
+public class NewFingerprintEnrolledException extends InvalidKeyException {
/**
* Constructs a new {@code NewFingerprintEnrolledException} without detail message and cause.
diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java
index f5f5f41..66f4dd8 100644
--- a/keystore/java/android/security/UserNotAuthenticatedException.java
+++ b/keystore/java/android/security/UserNotAuthenticatedException.java
@@ -16,11 +16,13 @@
package android.security;
+import java.security.InvalidKeyException;
+
/**
* Indicates that a cryptographic operation could not be performed because the user has not been
* authenticated recently enough.
*/
-public class UserNotAuthenticatedException extends CryptoOperationException {
+public class UserNotAuthenticatedException extends InvalidKeyException {
/**
* Constructs a new {@code UserNotAuthenticatedException} without detail message and cause.
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
index 7a88dee..a7046dd2 100644
--- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -18,7 +18,7 @@
import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
-import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.NativeConstants;
import com.android.org.conscrypt.OpenSSLEngine;
import android.test.AndroidTestCase;
@@ -768,7 +768,7 @@
assertAliases(new String[] {});
assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+ KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
null));
assertAliases(new String[] { TEST_ALIAS_1 });
@@ -797,7 +797,7 @@
assertAliases(new String[] {});
assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1,
- KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+ KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
null));
assertTrue("Should contain generated private key", mKeyStore.containsAlias(TEST_ALIAS_1));
@@ -1963,7 +1963,7 @@
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
- NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+ NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
@@ -2019,7 +2019,7 @@
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
- NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+ NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1,
TEST_SERIAL_1, TEST_DN_1, NOW, NOW_PLUS_10_YEARS);
@@ -2032,7 +2032,7 @@
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_2;
assertTrue(mAndroidKeyStore.generate(privateKeyAlias, KeyStore.UID_SELF,
- NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
+ NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED, null));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_2,
TEST_SERIAL_2, TEST_DN_2, NOW, NOW_PLUS_10_YEARS);
@@ -2064,7 +2064,7 @@
{
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
assertTrue(mAndroidKeyStore.generate(privateKeyAlias,
- android.security.KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024,
+ android.security.KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024,
android.security.KeyStore.FLAG_NONE, null));
X509Certificate cert =
@@ -2116,7 +2116,7 @@
assertAliases(new String[] { TEST_ALIAS_1, TEST_ALIAS_2 });
assertTrue(mAndroidKeyStore.generate(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3,
- KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
+ KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024, KeyStore.FLAG_ENCRYPTED,
null));
assertEquals("The keystore size should match expected", 3, mKeyStore.size());
@@ -2184,7 +2184,7 @@
private void setupKey() throws Exception {
final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + TEST_ALIAS_1;
assertTrue(mAndroidKeyStore
- .generate(privateKeyAlias, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA, 1024,
+ .generate(privateKeyAlias, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA, 1024,
KeyStore.FLAG_ENCRYPTED, null));
X509Certificate cert = generateCertificate(mAndroidKeyStore, TEST_ALIAS_1, TEST_SERIAL_1,
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 1a5552a..916b1ba 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -32,7 +32,7 @@
import android.test.AssertionFailedError;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.MediumTest;
-import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.NativeConstants;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
@@ -365,7 +365,7 @@
public void testGenerate_NotInitialized_Fail() throws Exception {
assertFalse("Should fail when keystore is not initialized",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
}
@@ -373,7 +373,7 @@
mKeyStore.password(TEST_PASSWD);
mKeyStore.lock();
assertFalse("Should fail when keystore is locked",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
}
@@ -381,7 +381,7 @@
assertTrue(mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key when unlocked",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
@@ -391,7 +391,7 @@
assertTrue(mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key when unlocked",
- mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -401,7 +401,7 @@
assertTrue(mKeyStore.password(TEST_PASSWD));
assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID,
- NativeCrypto.EVP_PKEY_RSA, RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
+ NativeConstants.EVP_PKEY_RSA, RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -447,7 +447,7 @@
public void testSign_Success() throws Exception {
mKeyStore.password(TEST_PASSWD);
- assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
@@ -458,7 +458,7 @@
public void testVerify_Success() throws Exception {
mKeyStore.password(TEST_PASSWD);
- assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
@@ -486,7 +486,7 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue("Should be able to grant key to other user",
@@ -520,7 +520,7 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue("Should be able to grant key to other user",
@@ -554,7 +554,7 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertFalse("Should not be able to revoke not existent grant",
@@ -566,7 +566,7 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue("Should be able to grant key to other user",
@@ -584,7 +584,7 @@
mKeyStore.password(TEST_PASSWD));
assertTrue("Should be able to generate key for testcase",
- mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue("Should be able to grant key to other user",
@@ -605,7 +605,7 @@
assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
@@ -644,7 +644,7 @@
assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeCrypto.EVP_PKEY_RSA,
+ assertTrue(mKeyStore.generate(TEST_KEYNAME, KeyStore.UID_SELF, NativeConstants.EVP_PKEY_RSA,
RSA_KEY_SIZE, KeyStore.FLAG_ENCRYPTED, null));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index 5fca8ec..836f868 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -24,6 +24,7 @@
thread/TaskManager.cpp \
utils/Blur.cpp \
utils/GLUtils.cpp \
+ utils/LinearAllocator.cpp \
utils/SortedListImpl.cpp \
AmbientShadow.cpp \
AnimationContext.cpp \
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index c92ab91..f535afb 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -127,7 +127,7 @@
}
void tryRecycleState(DeferredDisplayState* state) {
- mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState));
+ mAllocator.rewindIfLastAlloc(state);
}
/**
diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk
index 51898d2..b6f0baf 100644
--- a/libs/hwui/tests/Android.mk
+++ b/libs/hwui/tests/Android.mk
@@ -34,16 +34,3 @@
tests/main.cpp
include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
-LOCAL_MODULE := hwui_unit_tests
-LOCAL_MODULE_TAGS := tests
-
-include $(LOCAL_PATH)/Android.common.mk
-
-LOCAL_SRC_FILES += \
- tests/ClipAreaTests.cpp \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk
new file mode 100644
index 0000000..51601b0
--- /dev/null
+++ b/libs/hwui/unit_tests/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+local_target_dir := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PATH:= $(call my-dir)/..
+
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
+LOCAL_MODULE := hwui_unit_tests
+LOCAL_MODULE_TAGS := tests
+
+include $(LOCAL_PATH)/Android.common.mk
+
+LOCAL_SRC_FILES += \
+ unit_tests/ClipAreaTests.cpp \
+ unit_tests/LinearAllocatorTests.cpp \
+ unit_tests/main.cpp
+
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp
similarity index 100%
rename from libs/hwui/tests/ClipAreaTests.cpp
rename to libs/hwui/unit_tests/ClipAreaTests.cpp
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
new file mode 100644
index 0000000..b3959d1
--- /dev/null
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <utils/LinearAllocator.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+struct SimplePair {
+ int one = 1;
+ int two = 2;
+};
+
+class SignalingDtor {
+public:
+ SignalingDtor() {
+ mDestroyed = nullptr;
+ }
+ SignalingDtor(bool* destroyedSignal) {
+ mDestroyed = destroyedSignal;
+ *mDestroyed = false;
+ }
+ virtual ~SignalingDtor() {
+ if (mDestroyed) {
+ *mDestroyed = true;
+ }
+ }
+ void setSignal(bool* destroyedSignal) {
+ mDestroyed = destroyedSignal;
+ }
+private:
+ bool* mDestroyed;
+};
+
+TEST(LinearAllocator, alloc) {
+ LinearAllocator la;
+ EXPECT_EQ(0u, la.usedSize());
+ la.alloc(64);
+ // There's some internal tracking as well as padding
+ // so the usedSize isn't strictly defined
+ EXPECT_LE(64u, la.usedSize());
+ EXPECT_GT(80u, la.usedSize());
+ auto pair = la.alloc<SimplePair>();
+ EXPECT_LE(64u + sizeof(SimplePair), la.usedSize());
+ EXPECT_GT(80u + sizeof(SimplePair), la.usedSize());
+ EXPECT_EQ(1, pair->one);
+ EXPECT_EQ(2, pair->two);
+}
+
+TEST(LinearAllocator, dtor) {
+ bool destroyed[10];
+ {
+ LinearAllocator la;
+ for (int i = 0; i < 5; i++) {
+ la.alloc<SignalingDtor>()->setSignal(destroyed + i);
+ la.alloc<SimplePair>();
+ }
+ la.alloc(100);
+ for (int i = 0; i < 5; i++) {
+ auto sd = new (la) SignalingDtor(destroyed + 5 + i);
+ la.autoDestroy(sd);
+ new (la) SimplePair();
+ }
+ la.alloc(100);
+ for (int i = 0; i < 10; i++) {
+ EXPECT_FALSE(destroyed[i]);
+ }
+ }
+ for (int i = 0; i < 10; i++) {
+ EXPECT_TRUE(destroyed[i]);
+ }
+}
+
+TEST(LinearAllocator, rewind) {
+ bool destroyed;
+ {
+ LinearAllocator la;
+ auto addr = la.alloc(100);
+ EXPECT_LE(100u, la.usedSize());
+ la.rewindIfLastAlloc(addr, 100);
+ EXPECT_GT(16u, la.usedSize());
+ size_t emptySize = la.usedSize();
+ auto sigdtor = la.alloc<SignalingDtor>();
+ sigdtor->setSignal(&destroyed);
+ EXPECT_FALSE(destroyed);
+ EXPECT_LE(emptySize, la.usedSize());
+ la.rewindIfLastAlloc(sigdtor);
+ EXPECT_TRUE(destroyed);
+ EXPECT_EQ(emptySize, la.usedSize());
+ destroyed = false;
+ }
+ // Checking for a double-destroy case
+ EXPECT_EQ(destroyed, false);
+}
diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt
new file mode 100755
index 0000000..a2d6a34
--- /dev/null
+++ b/libs/hwui/unit_tests/how_to_run.txt
@@ -0,0 +1,4 @@
+mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests &&
+adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \
+ /data/nativetest/hwui_unit_tests/hwui_unit_tests &&
+adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests
diff --git a/libs/hwui/unit_tests/main.cpp b/libs/hwui/unit_tests/main.cpp
new file mode 100644
index 0000000..c9b9636
--- /dev/null
+++ b/libs/hwui/unit_tests/main.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
new file mode 100644
index 0000000..59b12cf
--- /dev/null
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_NDEBUG 1
+
+#include "utils/LinearAllocator.h"
+
+#include <stdlib.h>
+#include <utils/Log.h>
+
+
+// The ideal size of a page allocation (these need to be multiples of 8)
+#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
+#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
+
+// The maximum amount of wasted space we can have per page
+// Allocations exceeding this will have their own dedicated page
+// If this is too low, we will malloc too much
+// Too high, and we may waste too much space
+// Must be smaller than INITIAL_PAGE_SIZE
+#define MAX_WASTE_SIZE ((size_t)1024)
+
+#if ALIGN_DOUBLE
+#define ALIGN_SZ (sizeof(double))
+#else
+#define ALIGN_SZ (sizeof(int))
+#endif
+
+#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1))
+#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
+
+#if LOG_NDEBUG
+#define ADD_ALLOCATION(size)
+#define RM_ALLOCATION(size)
+#else
+#include <utils/Thread.h>
+#include <utils/Timers.h>
+static size_t s_totalAllocations = 0;
+static nsecs_t s_nextLog = 0;
+static android::Mutex s_mutex;
+
+static void _logUsageLocked() {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now > s_nextLog) {
+ s_nextLog = now + milliseconds_to_nanoseconds(10);
+ ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+ }
+}
+
+static void _addAllocation(size_t size) {
+ android::AutoMutex lock(s_mutex);
+ s_totalAllocations += size;
+ _logUsageLocked();
+}
+
+#define ADD_ALLOCATION(size) _addAllocation(size);
+#define RM_ALLOCATION(size) _addAllocation(-size);
+#endif
+
+#define min(x,y) (((x) < (y)) ? (x) : (y))
+
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) {
+ return la.alloc(size);
+}
+
+namespace android {
+namespace uirenderer {
+
+class LinearAllocator::Page {
+public:
+ Page* next() { return mNextPage; }
+ void setNext(Page* next) { mNextPage = next; }
+
+ Page()
+ : mNextPage(0)
+ {}
+
+ void* operator new(size_t /*size*/, void* buf) { return buf; }
+
+ void* start() {
+ return (void*) (((size_t)this) + sizeof(Page));
+ }
+
+ void* end(int pageSize) {
+ return (void*) (((size_t)start()) + pageSize);
+ }
+
+private:
+ Page(const Page& /*other*/) {}
+ Page* mNextPage;
+};
+
+LinearAllocator::LinearAllocator()
+ : mPageSize(INITIAL_PAGE_SIZE)
+ , mMaxAllocSize(MAX_WASTE_SIZE)
+ , mNext(0)
+ , mCurrentPage(0)
+ , mPages(0)
+ , mTotalAllocated(0)
+ , mWastedSpace(0)
+ , mPageCount(0)
+ , mDedicatedPageCount(0) {}
+
+LinearAllocator::~LinearAllocator(void) {
+ while (mDtorList) {
+ auto node = mDtorList;
+ mDtorList = node->next;
+ node->dtor(node->addr);
+ }
+ Page* p = mPages;
+ while (p) {
+ Page* next = p->next();
+ p->~Page();
+ free(p);
+ RM_ALLOCATION(mPageSize);
+ p = next;
+ }
+}
+
+void* LinearAllocator::start(Page* p) {
+ return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+}
+
+void* LinearAllocator::end(Page* p) {
+ return ((char*)p) + mPageSize;
+}
+
+bool LinearAllocator::fitsInCurrentPage(size_t size) {
+ return mNext && ((char*)mNext + size) <= end(mCurrentPage);
+}
+
+void LinearAllocator::ensureNext(size_t size) {
+ if (fitsInCurrentPage(size)) return;
+
+ if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
+ mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+ mPageSize = ALIGN(mPageSize);
+ }
+ mWastedSpace += mPageSize;
+ Page* p = newPage(mPageSize);
+ if (mCurrentPage) {
+ mCurrentPage->setNext(p);
+ }
+ mCurrentPage = p;
+ if (!mPages) {
+ mPages = mCurrentPage;
+ }
+ mNext = start(mCurrentPage);
+}
+
+void* LinearAllocator::alloc(size_t size) {
+ size = ALIGN(size);
+ if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
+ ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
+ // Allocation is too large, create a dedicated page for the allocation
+ Page* page = newPage(size);
+ mDedicatedPageCount++;
+ page->setNext(mPages);
+ mPages = page;
+ if (!mCurrentPage)
+ mCurrentPage = mPages;
+ return start(page);
+ }
+ ensureNext(size);
+ void* ptr = mNext;
+ mNext = ((char*)mNext) + size;
+ mWastedSpace -= size;
+ return ptr;
+}
+
+void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) {
+ static_assert(std::is_standard_layout<DestructorNode>::value,
+ "DestructorNode must have standard layout");
+ static_assert(std::is_trivially_destructible<DestructorNode>::value,
+ "DestructorNode must be trivially destructable");
+ auto node = new (*this) DestructorNode();
+ node->dtor = dtor;
+ node->addr = addr;
+ node->next = mDtorList;
+ mDtorList = node;
+}
+
+void LinearAllocator::runDestructorFor(void* addr) {
+ auto node = mDtorList;
+ DestructorNode* previous = nullptr;
+ while (node) {
+ if (node->addr == addr) {
+ if (previous) {
+ previous->next = node->next;
+ } else {
+ mDtorList = node->next;
+ }
+ node->dtor(node->addr);
+ rewindIfLastAlloc(node, sizeof(DestructorNode));
+ break;
+ }
+ previous = node;
+ node = node->next;
+ }
+}
+
+void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
+ // First run the destructor as running the destructor will
+ // also rewind for the DestructorNode allocation which will
+ // have been allocated after this void* if it has a destructor
+ runDestructorFor(ptr);
+ // Don't bother rewinding across pages
+ allocSize = ALIGN(allocSize);
+ if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
+ && ptr == ((char*)mNext - allocSize)) {
+ mWastedSpace += allocSize;
+ mNext = ptr;
+ }
+}
+
+LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
+ pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
+ ADD_ALLOCATION(pageSize);
+ mTotalAllocated += pageSize;
+ mPageCount++;
+ void* buf = malloc(pageSize);
+ return new (buf) Page();
+}
+
+static const char* toSize(size_t value, float& result) {
+ if (value < 2000) {
+ result = value;
+ return "B";
+ }
+ if (value < 2000000) {
+ result = value / 1024.0f;
+ return "KB";
+ }
+ result = value / 1048576.0f;
+ return "MB";
+}
+
+void LinearAllocator::dumpMemoryStats(const char* prefix) {
+ float prettySize;
+ const char* prettySuffix;
+ prettySuffix = toSize(mTotalAllocated, prettySize);
+ ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix);
+ prettySuffix = toSize(mWastedSpace, prettySize);
+ ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix,
+ (float) mWastedSpace / (float) mTotalAllocated * 100.0f);
+ ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
new file mode 100644
index 0000000..d90dd82
--- /dev/null
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_LINEARALLOCATOR_H
+#define ANDROID_LINEARALLOCATOR_H
+
+#include <stddef.h>
+#include <type_traits>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids
+ * the overhead of malloc when many objects are allocated. It is most useful when creating many
+ * small objects with a similar lifetime, and doesn't add significant overhead for large
+ * allocations.
+ */
+class LinearAllocator {
+public:
+ LinearAllocator();
+ ~LinearAllocator();
+
+ /**
+ * Reserves and returns a region of memory of at least size 'size', aligning as needed.
+ * Typically this is used in an object's overridden new() method or as a replacement for malloc.
+ *
+ * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
+ * delete() on an object stored in a buffer is needed, it should be overridden to use
+ * rewindIfLastAlloc()
+ */
+ void* alloc(size_t size);
+
+ /**
+ * Allocates an instance of the template type with the default constructor
+ * and adds it to the automatic destruction list.
+ */
+ template<class T>
+ T* alloc() {
+ T* ret = new (*this) T;
+ autoDestroy(ret);
+ return ret;
+ }
+
+ /**
+ * Adds the pointer to the tracking list to have its destructor called
+ * when the LinearAllocator is destroyed.
+ */
+ template<class T>
+ void autoDestroy(T* addr) {
+ if (!std::is_trivially_destructible<T>::value) {
+ auto dtor = [](void* addr) { ((T*)addr)->~T(); };
+ addToDestructionList(dtor, addr);
+ }
+ }
+
+ /**
+ * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its
+ * state if possible.
+ */
+ void rewindIfLastAlloc(void* ptr, size_t allocSize);
+
+ /**
+ * Same as rewindIfLastAlloc(void*, size_t)
+ */
+ template<class T>
+ void rewindIfLastAlloc(T* ptr) {
+ rewindIfLastAlloc((void*)ptr, sizeof(T));
+ }
+
+ /**
+ * Dump memory usage statistics to the log (allocated and wasted space)
+ */
+ void dumpMemoryStats(const char* prefix = "");
+
+ /**
+ * The number of bytes used for buffers allocated in the LinearAllocator (does not count space
+ * wasted)
+ */
+ size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
+
+private:
+ LinearAllocator(const LinearAllocator& other);
+
+ class Page;
+ typedef void (*Destructor)(void* addr);
+ struct DestructorNode {
+ Destructor dtor;
+ void* addr;
+ DestructorNode* next = nullptr;
+ };
+
+ void addToDestructionList(Destructor, void* addr);
+ void runDestructorFor(void* addr);
+ Page* newPage(size_t pageSize);
+ bool fitsInCurrentPage(size_t size);
+ void ensureNext(size_t size);
+ void* start(Page *p);
+ void* end(Page* p);
+
+ size_t mPageSize;
+ size_t mMaxAllocSize;
+ void* mNext;
+ Page* mCurrentPage;
+ Page* mPages;
+ DestructorNode* mDtorList = nullptr;
+
+ // Memory usage tracking
+ size_t mTotalAllocated;
+ size_t mWastedSpace;
+ size_t mPageCount;
+ size_t mDedicatedPageCount;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la);
+
+#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 8bbfb51..201a796 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -20,6 +20,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
+import java.util.Collection;
import java.util.Iterator;
import android.annotation.IntDef;
@@ -32,6 +33,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Log;
/**
@@ -57,7 +59,7 @@
/** Minimum value for sample rate */
private static final int SAMPLE_RATE_HZ_MIN = 4000;
/** Maximum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MAX = 96000;
+ private static final int SAMPLE_RATE_HZ_MAX = 192000;
/**
* indicates AudioRecord state is not successfully initialized.
@@ -113,6 +115,11 @@
*/
private static final int NATIVE_EVENT_NEW_POS = 3;
+ /**
+ * Event id denotes when the routing changes.
+ */
+ private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
+
private final static String TAG = "android.media.AudioRecord";
/** @hide */
@@ -683,7 +690,7 @@
}
/**
- * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_8BIT},
+ * Returns the configured audio data encoding. See {@link AudioFormat#ENCODING_PCM_8BIT},
* {@link AudioFormat#ENCODING_PCM_16BIT}, and {@link AudioFormat#ENCODING_PCM_FLOAT}.
*/
public int getAudioFormat() {
@@ -691,15 +698,37 @@
}
/**
- * Returns the configured channel configuration.
- * See {@link AudioFormat#CHANNEL_IN_MONO}
+ * Returns the configured channel position mask.
+ * <p> See {@link AudioFormat#CHANNEL_IN_MONO}
* and {@link AudioFormat#CHANNEL_IN_STEREO}.
+ * This method may return {@link AudioFormat#CHANNEL_INVALID} if
+ * a channel index mask is used.
+ * Consider {@link #getFormat()} instead, to obtain an {@link AudioFormat},
+ * which contains both the channel position mask and the channel index mask.
*/
public int getChannelConfiguration() {
return mChannelMask;
}
/**
+ * Returns the configured <code>AudioRecord</code> format.
+ * @return an {@link AudioFormat} containing the
+ * <code>AudioRecord</code> parameters at the time of configuration.
+ */
+ public @NonNull AudioFormat getFormat() {
+ AudioFormat.Builder builder = new AudioFormat.Builder()
+ .setSampleRate(mSampleRate)
+ .setEncoding(mAudioFormat);
+ if (mChannelMask != AudioFormat.CHANNEL_INVALID) {
+ builder.setChannelMask(mChannelMask);
+ }
+ if (mChannelIndexMask != AudioFormat.CHANNEL_INVALID /* 0 */) {
+ builder.setChannelIndexMask(mChannelIndexMask);
+ }
+ return builder.build();
+ }
+
+ /**
* Returns the configured number of channels.
*/
public int getChannelCount() {
@@ -1127,7 +1156,7 @@
* Sets the listener the AudioRecord notifies when a previously set marker is reached or
* for each periodic record head position update.
* Use this method to receive AudioRecord events in the Handler associated with another
- * thread than the one in which you created the AudioTrack instance.
+ * thread than the one in which you created the AudioRecord instance.
* @param listener
* @param handler the Handler that will receive the event notification messages.
*/
@@ -1168,6 +1197,115 @@
}
+ //--------------------------------------------------------------------------
+ // (Re)Routing Info
+ //--------------------
+ /**
+ * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
+ */
+ public AudioDeviceInfo getRoutedDevice() {
+ return null;
+ }
+
+ /**
+ * The message sent to apps when the routing of this AudioRecord changes if they provide
+ * a {#link Handler} object to addOnAudioRecordRoutingListener().
+ */
+ private ArrayMap<OnAudioRecordRoutingListener, NativeRoutingEventHandlerDelegate>
+ mRoutingChangeListeners =
+ new ArrayMap<OnAudioRecordRoutingListener, NativeRoutingEventHandlerDelegate>();
+
+ /**
+ * Adds an {@link OnAudioRecordRoutingListener} to receive notifications of routing changes
+ * on this AudioRecord.
+ */
+ public void addOnAudioRecordRoutingListener(OnAudioRecordRoutingListener listener,
+ android.os.Handler handler) {
+ if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+ synchronized (mRoutingChangeListeners) {
+ mRoutingChangeListeners.put(
+ listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ }
+ }
+ }
+
+ /**
+ * Removes an {@link OnAudioRecordRoutingListener} which has been previously added
+ * to receive notifications of changes to the set of connected audio devices.
+ */
+ public void removeOnAudioRecordRoutingListener(OnAudioRecordRoutingListener listener) {
+ synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.containsKey(listener)) {
+ mRoutingChangeListeners.remove(listener);
+ }
+ }
+ }
+
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread
+ */
+ private class NativeRoutingEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeRoutingEventHandlerDelegate(final AudioRecord record,
+ final OnAudioRecordRoutingListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ // no given handler, use the looper the AudioRecord was created in
+ looper = mInitializationLooper;
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (record == null) {
+ return;
+ }
+ switch(msg.what) {
+ case NATIVE_EVENT_ROUTING_CHANGE:
+ if (listener != null) {
+ listener.onAudioRecordRouting(record);
+ }
+ break;
+ default:
+ loge("Unknown native event type: " + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+ }
+ /**
+ * Sends device list change notification to all listeners.
+ */
+ private void broadcastRoutingChange() {
+ Collection<NativeRoutingEventHandlerDelegate> values;
+ synchronized (mRoutingChangeListeners) {
+ values = mRoutingChangeListeners.values();
+ }
+ for(NativeRoutingEventHandlerDelegate delegate : values) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
+ }
+ }
+ }
+
/**
* Sets the period at which the listener is called, if set with
* {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or
@@ -1184,6 +1322,39 @@
}
+ //--------------------------------------------------------------------------
+ // Explicit Routing
+ //--------------------
+ private AudioDeviceInfo mPreferredDevice = null;
+
+ /**
+ * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
+ * the input to this AudioRecord.
+ * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio source.
+ * If deviceInfo is null, default routing is restored.
+ * @return true if successful, false if the specified {@link AudioDeviceInfo} is non-null and
+ * does not correspond to a valid audio input device.
+ */
+ public boolean setPreferredInputDevice(AudioDeviceInfo deviceInfo) {
+ // Do some validation....
+ if (deviceInfo != null && !deviceInfo.isSource()) {
+ return false;
+ }
+
+ mPreferredDevice = deviceInfo;
+ int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0;
+
+ return native_setInputDevice(preferredDeviceId);
+ }
+
+ /**
+ * Returns the selected input specified by {@link #setPreferredInputDevice}. Note that this
+ * is not guarenteed to correspond to the actual device being used for recording.
+ */
+ public AudioDeviceInfo getPreferredInputDevice() {
+ return mPreferredDevice;
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
@@ -1314,6 +1485,8 @@
static private native final int native_get_min_buff_size(
int sampleRateInHz, int channelCount, int audioFormat);
+ private native final boolean native_setInputDevice(int deviceId);
+
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 093ff26..3577357 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,6 +23,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
+import java.util.Collection;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -37,6 +38,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.app.IAppOpsService;
@@ -94,7 +96,7 @@
/** Minimum value for sample rate */
private static final int SAMPLE_RATE_HZ_MIN = 4000;
/** Maximum value for sample rate */
- private static final int SAMPLE_RATE_HZ_MAX = 96000;
+ private static final int SAMPLE_RATE_HZ_MAX = 192000;
/** Maximum value for AudioTrack channel count */
private static final int CHANNEL_COUNT_MAX = 8;
@@ -176,6 +178,12 @@
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
+ /**
+ * Event id denotes when the routing changes.
+ */
+ private final static int NATIVE_EVENT_ROUTING_CHANGE = 1000;
+
+
private final static String TAG = "android.media.AudioTrack";
@@ -224,7 +232,7 @@
/**
* Handler for events coming from the native code.
*/
- private NativeEventHandlerDelegate mEventHandlerDelegate;
+ private NativePositionEventHandlerDelegate mEventHandlerDelegate;
/**
* Looper associated with the thread that creates the AudioTrack instance.
*/
@@ -970,8 +978,8 @@
}
/**
- * Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT}
- * and {@link AudioFormat#ENCODING_PCM_8BIT}.
+ * Returns the configured audio data encoding. See {@link AudioFormat#ENCODING_PCM_8BIT},
+ * {@link AudioFormat#ENCODING_PCM_16BIT}, and {@link AudioFormat#ENCODING_PCM_FLOAT}.
*/
public int getAudioFormat() {
return mAudioFormat;
@@ -990,14 +998,36 @@
/**
* Returns the configured channel position mask.
- * For example, refer to {@link AudioFormat#CHANNEL_OUT_MONO},
+ * <p> For example, refer to {@link AudioFormat#CHANNEL_OUT_MONO},
* {@link AudioFormat#CHANNEL_OUT_STEREO}, {@link AudioFormat#CHANNEL_OUT_5POINT1}.
+ * This method may return {@link AudioFormat#CHANNEL_INVALID} if
+ * a channel index mask is used. Consider
+ * {@link #getFormat()} instead, to obtain an {@link AudioFormat},
+ * which contains both the channel position mask and the channel index mask.
*/
public int getChannelConfiguration() {
return mChannelConfiguration;
}
/**
+ * Returns the configured <code>AudioTrack</code> format.
+ * @return an {@link AudioFormat} containing the
+ * <code>AudioTrack</code> parameters at the time of configuration.
+ */
+ public @NonNull AudioFormat getFormat() {
+ AudioFormat.Builder builder = new AudioFormat.Builder()
+ .setSampleRate(mSampleRate)
+ .setEncoding(mAudioFormat);
+ if (mChannelConfiguration != AudioFormat.CHANNEL_INVALID) {
+ builder.setChannelMask(mChannelConfiguration);
+ }
+ if (mChannelIndexMask != AudioFormat.CHANNEL_INVALID /* 0 */) {
+ builder.setChannelIndexMask(mChannelIndexMask);
+ }
+ return builder.build();
+ }
+
+ /**
* Returns the configured number of channels.
*/
public int getChannelCount() {
@@ -1243,7 +1273,7 @@
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener,
Handler handler) {
if (listener != null) {
- mEventHandlerDelegate = new NativeEventHandlerDelegate(this, listener, handler);
+ mEventHandlerDelegate = new NativePositionEventHandlerDelegate(this, listener, handler);
} else {
mEventHandlerDelegate = null;
}
@@ -1418,7 +1448,8 @@
* the position values have different meanings.
* <br>
* If looping is currently enabled and the new position is greater than or equal to the
- * loop end marker, the behavior varies by API level: for API level 22 and above,
+ * loop end marker, the behavior varies by API level:
+ * as of {@link android.os.Build.VERSION_CODES#MNC},
* the looping is first disabled and then the position is set.
* For earlier API levels, the behavior is unspecified.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
@@ -1455,7 +1486,7 @@
* {@link #ERROR_BAD_VALUE} is returned.
* The loop range is the interval [startInFrames, endInFrames).
* <br>
- * For API level 22 and above, the position is left unchanged,
+ * As of {@link android.os.Build.VERSION_CODES#MNC}, the position is left unchanged,
* unless it is greater than or equal to the loop end marker, in which case
* it is forced to the loop start marker.
* For earlier API levels, the effect on position is unspecified.
@@ -2002,12 +2033,12 @@
* The track must be stopped or paused, and
* the track's creation mode must be {@link #MODE_STATIC}.
* <p>
- * For API level 22 and above, also resets the value returned by
+ * As of {@link android.os.Build.VERSION_CODES#MNC}, also resets the value returned by
* {@link #getPlaybackHeadPosition()} to zero.
* For earlier API levels, the reset behavior is unspecified.
* <p>
- * {@link #setPlaybackHeadPosition(int)} to zero
- * is recommended instead when the reset of {@link #getPlaybackHeadPosition} is not needed.
+ * Use {@link #setPlaybackHeadPosition(int)} with a zero position
+ * if the reset of <code>getPlaybackHeadPosition()</code> is not needed.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
@@ -2082,33 +2113,93 @@
private AudioDeviceInfo mPreferredDevice = null;
/**
- * Specifies an audio device (via and {@link AudioDeviceInfo} object) to route
+ * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
* the output from this AudioTrack.
- * @param deviceSpec The {@link AudioDeviceInfo} specifying the physical audio device.
- * If deviceSpec is null, default routing is restored.
+ * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink.
+ * If deviceInfo is null, default routing is restored.
* @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
* does not correspond to a valid audio output device.
*/
- public boolean setPreferredOutputDevice(AudioDeviceInfo deviceSpec) {
+ public boolean setPreferredOutputDevice(AudioDeviceInfo deviceInfo) {
// Do some validation....
- if (deviceSpec != null && !deviceSpec.isSink()) {
+ if (deviceInfo != null && !deviceInfo.isSink()) {
return false;
}
- mPreferredDevice = deviceSpec;
- int routingDeviceId = mPreferredDevice != null ? deviceSpec.getId() : 0;
+ mPreferredDevice = deviceInfo;
+ int preferredDeviceId = mPreferredDevice != null ? deviceInfo.getId() : 0;
- return native_setOutputDevice(routingDeviceId);
+ return native_setOutputDevice(preferredDeviceId);
}
/**
* Returns the selected output specified by {@link #setPreferredOutputDevice}. Note that this
- * is not guarenteed to correspond to the actual device being used for playback.
+ * is not guaranteed to correspond to the actual device being used for playback.
*/
public AudioDeviceInfo getPreferredOutputDevice() {
return mPreferredDevice;
}
+ //--------------------------------------------------------------------------
+ // (Re)Routing Info
+ //--------------------
+ /**
+ * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
+ */
+ public AudioDeviceInfo getRoutedDevice() {
+ return null;
+ }
+
+ /**
+ * The message sent to apps when the routing of this AudioTrack changes if they provide
+ * a {#link Handler} object to addOnAudioTrackRoutingListener().
+ */
+ private ArrayMap<OnAudioTrackRoutingListener, NativeRoutingEventHandlerDelegate>
+ mRoutingChangeListeners =
+ new ArrayMap<OnAudioTrackRoutingListener, NativeRoutingEventHandlerDelegate>();
+
+ /**
+ * Adds an {@link OnAudioTrackRoutingListener} to receive notifications of routing changes
+ * on this AudioTrack.
+ */
+ public void addOnAudioTrackRoutingListener(OnAudioTrackRoutingListener listener,
+ android.os.Handler handler) {
+ if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
+ synchronized (mRoutingChangeListeners) {
+ mRoutingChangeListeners.put(
+ listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ }
+ }
+ }
+
+ /**
+ * Removes an {@link OnAudioTrackRoutingListener} which has been previously added
+ * to receive notifications of changes to the set of connected audio devices.
+ */
+ public void removeOnAudioTrackRoutingListener(OnAudioTrackRoutingListener listener) {
+ synchronized (mRoutingChangeListeners) {
+ if (mRoutingChangeListeners.containsKey(listener)) {
+ mRoutingChangeListeners.remove(listener);
+ }
+ }
+ }
+
+ /**
+ * Sends device list change notification to all listeners.
+ */
+ private void broadcastRoutingChange() {
+ Collection<NativeRoutingEventHandlerDelegate> values;
+ synchronized (mRoutingChangeListeners) {
+ values = mRoutingChangeListeners.values();
+ }
+ for(NativeRoutingEventHandlerDelegate delegate : values) {
+ Handler handler = delegate.getHandler();
+ if (handler != null) {
+ handler.sendEmptyMessage(NATIVE_EVENT_ROUTING_CHANGE);
+ }
+ }
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
@@ -2137,10 +2228,10 @@
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
*/
- private class NativeEventHandlerDelegate {
+ private class NativePositionEventHandlerDelegate {
private final Handler mHandler;
- NativeEventHandlerDelegate(final AudioTrack track,
+ NativePositionEventHandlerDelegate(final AudioTrack track,
final OnPlaybackPositionUpdateListener listener,
Handler handler) {
// find the looper for our new event handler
@@ -2188,6 +2279,55 @@
}
}
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate listener
+ * (potentially) handled in a different thread
+ */
+ private class NativeRoutingEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeRoutingEventHandlerDelegate(final AudioTrack track,
+ final OnAudioTrackRoutingListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ // no given handler, use the looper the AudioTrack was created in
+ looper = mInitializationLooper;
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (track == null) {
+ return;
+ }
+ switch(msg.what) {
+ case NATIVE_EVENT_ROUTING_CHANGE:
+ if (listener != null) {
+ listener.onAudioTrackRouting(track);
+ }
+ break;
+ default:
+ loge("Unknown native event type: " + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+ }
//---------------------------------------------------------
// Java methods called from the native side
@@ -2201,7 +2341,7 @@
return;
}
- NativeEventHandlerDelegate delegate = track.mEventHandlerDelegate;
+ NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
if (delegate != null) {
Handler handler = delegate.getHandler();
if (handler != null) {
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 9ae468a..195c987 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -50,10 +50,25 @@
/**
* @hide
*/
+ protected boolean mIsImageValid = false;
+
+ /**
+ * @hide
+ */
protected Image() {
}
/**
+ * Throw IllegalStateException if the image is invalid (already closed).
+ *
+ * @hide
+ */
+ protected void throwISEIfImageIsInvalid() {
+ if (!mIsImageValid) {
+ throw new IllegalStateException("Image is already closed");
+ }
+ }
+ /**
* Get the format for this image. This format determines the number of
* ByteBuffers needed to represent the image, and the general layout of the
* pixel data in each in ByteBuffer.
@@ -86,6 +101,38 @@
* Each plane has its own row stride and pixel stride.</td>
* </tr>
* <tr>
+ * <td>{@link android.graphics.ImageFormat#YUV_422_888 YUV_422_888}</td>
+ * <td>3</td>
+ * <td>A luminance plane followed by the Cb and Cr chroma planes.
+ * The chroma planes have half the width and the full height of the luminance
+ * plane (4:2:2 subsampling). Each pixel sample in each plane has 8 bits.
+ * Each plane has its own row stride and pixel stride.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#YUV_444_888 YUV_444_888}</td>
+ * <td>3</td>
+ * <td>A luminance plane followed by the Cb and Cr chroma planes.
+ * The chroma planes have the same width and height as that of the luminance
+ * plane (4:4:4 subsampling). Each pixel sample in each plane has 8 bits.
+ * Each plane has its own row stride and pixel stride.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#FLEX_RGB_888 FLEX_RGB_888}</td>
+ * <td>3</td>
+ * <td>A R (red) plane followed by the G (green) and B (blue) planes.
+ * All planes have the same widths and heights.
+ * Each pixel sample in each plane has 8 bits.
+ * Each plane has its own row stride and pixel stride.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#FLEX_RGBA_8888 FLEX_RGBA_8888}</td>
+ * <td>4</td>
+ * <td>A R (red) plane followed by the G (green), B (blue), and
+ * A (alpha) planes. All planes have the same widths and heights.
+ * Each pixel sample in each plane has 8 bits.
+ * Each plane has its own row stride and pixel stride.</td>
+ * </tr>
+ * <tr>
* <td>{@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}</td>
* <td>1</td>
* <td>A single plane of raw sensor image data, with 16 bits per color
@@ -128,7 +175,7 @@
* Set the timestamp associated with this frame.
* <p>
* The timestamp is measured in nanoseconds, and is normally monotonically
- * increasing. However, However, the behavior of the timestamp depends on
+ * increasing. However, the behavior of the timestamp depends on
* the destination of this image. See {@link android.hardware.Camera Camera}
* , {@link android.hardware.camera2.CameraDevice CameraDevice},
* {@link MediaPlayer} and {@link MediaCodec} for more details.
@@ -144,6 +191,7 @@
* @param timestamp The timestamp to be set for this image.
*/
public void setTimestamp(long timestamp) {
+ throwISEIfImageIsInvalid();
return;
}
@@ -155,6 +203,7 @@
* </p>
*/
public boolean isOpaque() {
+ throwISEIfImageIsInvalid();
return false;
}
@@ -167,6 +216,8 @@
* using coordinates in the largest-resolution plane.
*/
public Rect getCropRect() {
+ throwISEIfImageIsInvalid();
+
if (mCropRect == null) {
return new Rect(0, 0, getWidth(), getHeight());
} else {
@@ -181,6 +232,8 @@
* using coordinates in the largest-resolution plane.
*/
public void setCropRect(Rect cropRect) {
+ throwISEIfImageIsInvalid();
+
if (cropRect != null) {
cropRect = new Rect(cropRect); // make a copy
cropRect.intersect(0, 0, getWidth(), getHeight());
@@ -228,6 +281,8 @@
* a new owner.
*/
boolean isAttachable() {
+ throwISEIfImageIsInvalid();
+
return false;
}
@@ -247,6 +302,8 @@
* @return The owner of the Image.
*/
Object getOwner() {
+ throwISEIfImageIsInvalid();
+
return null;
}
@@ -262,6 +319,8 @@
* @return native context associated with this Image.
*/
long getNativeContext() {
+ throwISEIfImageIsInvalid();
+
return 0;
}
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 3d8f9a0..6d30208 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -130,7 +130,7 @@
* </p>
* <p>
* Opaque ImageReaders are more efficient to use when application access to
- * image data is not necessary, comparing to ImageReaders using a non-opaque
+ * image data is not necessary, compared to ImageReaders using a non-opaque
* format such as {@link ImageFormat#YUV_420_888 YUV_420_888}.
* </p>
*
@@ -368,7 +368,7 @@
switch (status) {
case ACQUIRE_SUCCESS:
si.createSurfacePlanes();
- si.setImageValid(true);
+ si.mIsImageValid = true;
case ACQUIRE_NO_BUFS:
case ACQUIRE_MAX_IMAGES:
break;
@@ -444,7 +444,7 @@
si.clearSurfacePlanes();
nativeReleaseImage(i);
- si.setImageValid(false);
+ si.mIsImageValid = false;
}
/**
@@ -686,7 +686,6 @@
private class SurfaceImage extends android.media.Image {
public SurfaceImage(int format) {
- mIsImageValid = false;
mFormat = format;
}
@@ -784,16 +783,6 @@
mIsDetached.getAndSet(detached);
}
- private void setImageValid(boolean isValid) {
- mIsImageValid = isValid;
- }
-
- private void throwISEIfImageIsInvalid() {
- if (!mIsImageValid) {
- throw new IllegalStateException("Image is already closed");
- }
- }
-
private void clearSurfacePlanes() {
if (mIsImageValid) {
for (int i = 0; i < mPlanes.length; i++) {
@@ -877,7 +866,6 @@
private long mTimestamp;
private SurfacePlane[] mPlanes;
- private boolean mIsImageValid;
private int mHeight = -1;
private int mWidth = -1;
private int mFormat = ImageFormat.UNKNOWN;
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index c18b463..f805339 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -29,7 +29,6 @@
import java.nio.NioUtils;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* <p>
@@ -204,7 +203,7 @@
WriterSurfaceImage newImage = new WriterSurfaceImage(this);
nativeDequeueInputImage(mNativeContext, newImage);
mDequeuedImages.add(newImage);
- newImage.setImageValid(true);
+ newImage.mIsImageValid = true;
return newImage;
}
@@ -260,7 +259,7 @@
throw new IllegalArgumentException("image shouldn't be null");
}
boolean ownedByMe = isImageOwnedByMe(image);
- if (ownedByMe && !(((WriterSurfaceImage) image).isImageValid())) {
+ if (ownedByMe && !(((WriterSurfaceImage) image).mIsImageValid)) {
throw new IllegalStateException("Image from ImageWriter is invalid");
}
@@ -312,7 +311,7 @@
// Do not call close here, as close is essentially cancel image.
WriterSurfaceImage wi = (WriterSurfaceImage) image;
wi.clearSurfacePlanes();
- wi.setImageValid(false);
+ wi.mIsImageValid = false;
}
}
@@ -555,7 +554,7 @@
WriterSurfaceImage wi = (WriterSurfaceImage) image;
- if (!wi.isImageValid()) {
+ if (!wi.mIsImageValid) {
throw new IllegalStateException("Image is invalid");
}
@@ -568,7 +567,7 @@
cancelImage(mNativeContext, image);
mDequeuedImages.remove(image);
wi.clearSurfacePlanes();
- wi.setImageValid(false);
+ wi.mIsImageValid = false;
}
private boolean isImageOwnedByMe(Image image) {
@@ -585,7 +584,6 @@
private static class WriterSurfaceImage extends android.media.Image {
private ImageWriter mOwner;
- private AtomicBoolean mIsImageValid = new AtomicBoolean(false);
// This field is used by native code, do not access or modify.
private long mNativeBuffer;
private int mNativeFenceFd = -1;
@@ -604,9 +602,8 @@
@Override
public int getFormat() {
- if (!mIsImageValid.get()) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
+
if (mFormat == -1) {
mFormat = nativeGetFormat();
}
@@ -615,9 +612,7 @@
@Override
public int getWidth() {
- if (!mIsImageValid.get()) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
if (mWidth == -1) {
mWidth = nativeGetWidth();
@@ -628,9 +623,7 @@
@Override
public int getHeight() {
- if (!mIsImageValid.get()) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
if (mHeight == -1) {
mHeight = nativeGetHeight();
@@ -641,36 +634,28 @@
@Override
public long getTimestamp() {
- if (!mIsImageValid.get()) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
return mTimestamp;
}
@Override
public void setTimestamp(long timestamp) {
- if (!mIsImageValid.get()) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
mTimestamp = timestamp;
}
@Override
public boolean isOpaque() {
- if (!mIsImageValid.get()) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
return getFormat() == ImageFormat.PRIVATE;
}
@Override
public Plane[] getPlanes() {
- if (!mIsImageValid.get()) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
if (mPlanes == null) {
int numPlanes = ImageUtils.getNumPlanesForFormat(getFormat());
@@ -682,9 +667,7 @@
@Override
boolean isAttachable() {
- if (!mIsImageValid.get()) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
// Don't allow Image to be detached from ImageWriter for now, as no
// detach API is exposed.
return false;
@@ -692,17 +675,21 @@
@Override
ImageWriter getOwner() {
+ throwISEIfImageIsInvalid();
+
return mOwner;
}
@Override
long getNativeContext() {
+ throwISEIfImageIsInvalid();
+
return mNativeBuffer;
}
@Override
public void close() {
- if (mIsImageValid.get()) {
+ if (mIsImageValid) {
getOwner().abortImage(this);
}
}
@@ -716,16 +703,8 @@
}
}
- private boolean isImageValid() {
- return mIsImageValid.get();
- }
-
- private void setImageValid(boolean isValid) {
- mIsImageValid.getAndSet(isValid);
- }
-
private void clearSurfacePlanes() {
- if (mIsImageValid.get()) {
+ if (mIsImageValid) {
for (int i = 0; i < mPlanes.length; i++) {
if (mPlanes[i] != null) {
mPlanes[i].clearBuffer();
@@ -756,26 +735,19 @@
@Override
public int getRowStride() {
- if (WriterSurfaceImage.this.isImageValid() == false) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
return mRowStride;
}
@Override
public int getPixelStride() {
- if (WriterSurfaceImage.this.isImageValid() == false) {
- throw new IllegalStateException("Image is already released");
- }
+ throwISEIfImageIsInvalid();
return mPixelStride;
}
@Override
public ByteBuffer getBuffer() {
- if (WriterSurfaceImage.this.isImageValid() == false) {
- throw new IllegalStateException("Image is already released");
- }
-
+ throwISEIfImageIsInvalid();
return mBuffer;
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 1f00c7b..d22cfda 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2059,7 +2059,6 @@
/** @hide */
public static class MediaImage extends Image {
private final boolean mIsReadOnly;
- private boolean mIsValid;
private final int mWidth;
private final int mHeight;
private final int mFormat;
@@ -2072,36 +2071,42 @@
private final static int TYPE_YUV = 1;
+ @Override
public int getFormat() {
- checkValid();
+ throwISEIfImageIsInvalid();
return mFormat;
}
+ @Override
public int getHeight() {
- checkValid();
+ throwISEIfImageIsInvalid();
return mHeight;
}
+ @Override
public int getWidth() {
- checkValid();
+ throwISEIfImageIsInvalid();
return mWidth;
}
+ @Override
public long getTimestamp() {
- checkValid();
+ throwISEIfImageIsInvalid();
return mTimestamp;
}
+ @Override
@NonNull
public Plane[] getPlanes() {
- checkValid();
+ throwISEIfImageIsInvalid();
return Arrays.copyOf(mPlanes, mPlanes.length);
}
+ @Override
public void close() {
- if (mIsValid) {
+ if (mIsImageValid) {
java.nio.NioUtils.freeDirectBuffer(mBuffer);
- mIsValid = false;
+ mIsImageValid = false;
}
}
@@ -2111,6 +2116,7 @@
* The crop rectangle specifies the region of valid pixels in the image,
* using coordinates in the largest-resolution plane.
*/
+ @Override
public void setCropRect(@Nullable Rect cropRect) {
if (mIsReadOnly) {
throw new ReadOnlyBufferException();
@@ -2118,11 +2124,6 @@
super.setCropRect(cropRect);
}
- private void checkValid() {
- if (!mIsValid) {
- throw new IllegalStateException("Image is already released");
- }
- }
private int readInt(@NonNull ByteBuffer buffer, boolean asLong) {
if (asLong) {
@@ -2137,7 +2138,7 @@
long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
mFormat = ImageFormat.YUV_420_888;
mTimestamp = timestamp;
- mIsValid = true;
+ mIsImageValid = true;
mIsReadOnly = buffer.isReadOnly();
mBuffer = buffer.duplicate();
@@ -2208,20 +2209,20 @@
@Override
public int getRowStride() {
- checkValid();
+ throwISEIfImageIsInvalid();
return mRowInc;
}
@Override
public int getPixelStride() {
- checkValid();
+ throwISEIfImageIsInvalid();
return mColInc;
}
@Override
@NonNull
public ByteBuffer getBuffer() {
- checkValid();
+ throwISEIfImageIsInvalid();
return mData;
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index ff1b57d..974c9af 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -160,21 +160,21 @@
public CodecProfileLevel[] profileLevels; // NOTE this array is modifiable by user
// from OMX_COLOR_FORMATTYPE
- /** @deprecated Use {@link #COLOR_Format24bitRGB888}. */
+ /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */
public static final int COLOR_FormatMonochrome = 1;
- /** @deprecated Use {@link #COLOR_Format24bitRGB888}. */
+ /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */
public static final int COLOR_Format8bitRGB332 = 2;
- /** @deprecated Use {@link #COLOR_Format24bitRGB888}. */
+ /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */
public static final int COLOR_Format12bitRGB444 = 3;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
+ /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */
public static final int COLOR_Format16bitARGB4444 = 4;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
+ /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */
public static final int COLOR_Format16bitARGB1555 = 5;
/**
* 16 bits per pixel RGB color format, with 5-bit red & blue and 6-bit green component.
* <p>
- * Using LSB 16-bit byte ordering, colors stored as Red 15:11, Green 10:5, Blue 4:0.
+ * Using 16-bit little-endian representation, colors stored as Red 15:11, Green 10:5, Blue 4:0.
* <pre>
* byte byte
* <--------- i --------> | <------ i + 1 ------>
@@ -184,64 +184,50 @@
* 0 4 5 7 0 2 3 7
* bit
* </pre>
+ *
+ * This format corresponds to {@link android.graphics.PixelFormat#RGB_565} and
+ * {@link android.graphics.ImageFormat#RGB_565}.
*/
public static final int COLOR_Format16bitRGB565 = 6;
/** @deprecated Use {@link #COLOR_Format16bitRGB565}. */
public static final int COLOR_Format16bitBGR565 = 7;
- /** @deprecated Use {@link #COLOR_Format24bitRGB888}. */
+ /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */
public static final int COLOR_Format18bitRGB666 = 8;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
+ /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */
public static final int COLOR_Format18bitARGB1665 = 9;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
+ /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */
public static final int COLOR_Format19bitARGB1666 = 10;
+ /** @deprecated Use {@link #COLOR_Format24bitBGR888} or {@link #COLOR_FormatRGBFlexible}. */
+ public static final int COLOR_Format24bitRGB888 = 11;
+
/**
* 24 bits per pixel RGB color format, with 8-bit red, green & blue components.
* <p>
- * Using LSB 24-bit byte ordering, colors stored as Red 23:16, Green 15:8, Blue 7:0.
+ * Using 24-bit little-endian representation, colors stored as Red 7:0, Green 15:8, Blue 23:16.
* <pre>
* byte byte byte
* <------ i -----> | <---- i+1 ----> | <---- i+2 ----->
* +-----------------+-----------------+-----------------+
- * | BLUE | GREEN | RED |
+ * | RED | GREEN | BLUE |
* +-----------------+-----------------+-----------------+
* </pre>
+ *
+ * This format corresponds to {@link android.graphics.PixelFormat#RGB_888}, and can also be
+ * represented as a flexible format by {@link #COLOR_FormatRGBFlexible}.
*/
- public static final int COLOR_Format24bitRGB888 = 11;
- /** @deprecated Use {@link #COLOR_Format24bitRGB888}. */
public static final int COLOR_Format24bitBGR888 = 12;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
+ /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */
public static final int COLOR_Format24bitARGB1887 = 13;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
+ /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */
public static final int COLOR_Format25bitARGB1888 = 14;
/**
- * 32 bits per pixel ARGB color format, with 8-bit alpha, red, green & blue components.
- * <p>
- * Using LSB 32-bit byte ordering, colors stored as Alpha 31:24, Red 23:16, Green 15:8,
- * Blue 7:0.
- * <pre>
- * byte byte byte byte
- * <------ i -----> | <---- i+1 ----> | <---- i+2 ----> | <---- i+3 ----->
- * +-----------------+-----------------+-----------------+-----------------+
- * | BLUE | GREEN | RED | ALPHA |
- * +-----------------+-----------------+-----------------+-----------------+
- * </pre>
+ * @deprecated Use {@link #COLOR_Format32bitABGR8888} Or {@link #COLOR_FormatRGBAFlexible}.
*/
public static final int COLOR_Format32bitBGRA8888 = 15;
-
/**
- * 32 bits per pixel ARGB color format, with 8-bit alpha, red, green & blue components.
- * <p>
- * Using LSB 32-bit byte ordering, colors stored as Alpha 31:24, Red 7:0, Green 15:8,
- * Blue 23:16.
- * <pre>
- * byte byte byte byte
- * <------ i -----> | <---- i+1 ----> | <---- i+2 ----> | <---- i+3 ----->
- * +-----------------+-----------------+-----------------+-----------------+
- * | RED | GREEN | BLUE | ALPHA |
- * +-----------------+-----------------+-----------------+-----------------+
- * </pre>
+ * @deprecated Use {@link #COLOR_Format32bitABGR8888} Or {@link #COLOR_FormatRGBAFlexible}.
*/
public static final int COLOR_Format32bitARGB8888 = 16;
/** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
@@ -271,10 +257,24 @@
/** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
public static final int COLOR_FormatCrYCbY = 28;
+ /** @deprecated Use {@link #COLOR_FormatYUV444Flexible}. */
public static final int COLOR_FormatYUV444Interleaved = 29;
+ /**
+ * SMIA 8-bit Bayer format.
+ * Each byte represents the top 8-bits of a 10-bit signal.
+ */
public static final int COLOR_FormatRawBayer8bit = 30;
+ /**
+ * SMIA 10-bit Bayer format.
+ */
public static final int COLOR_FormatRawBayer10bit = 31;
+
+ /**
+ * SMIA 8-bit compressed Bayer format.
+ * Each byte represents a sample from the 10-bit signal that is compressed into 8-bits
+ * using DPCM/PCM compression, as defined by the SMIA Functional Specification.
+ */
public static final int COLOR_FormatRawBayer8bitcompressed = 32;
/** @deprecated Use {@link #COLOR_FormatL8}. */
@@ -286,6 +286,7 @@
* 8 bits per pixel Y color format.
* <p>
* Each byte contains a single pixel.
+ * This format corresponds to {@link android.graphics.PixelFormat#L_8}.
*/
public static final int COLOR_FormatL8 = 35;
@@ -303,7 +304,7 @@
* </pre>
*/
public static final int COLOR_FormatL16 = 36;
- /** @deprecated Use {@link #COLOR_FormatL32}. */
+ /** @deprecated Use {@link #COLOR_FormatL16}. */
public static final int COLOR_FormatL24 = 37;
/**
@@ -318,6 +319,8 @@
* 0 7 0 7 0 7 0 7
* bit
* </pre>
+ *
+ * @deprecated Use {@link #COLOR_FormatL16}.
*/
public static final int COLOR_FormatL32 = 38;
@@ -326,12 +329,12 @@
/** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
public static final int COLOR_FormatYUV422PackedSemiPlanar = 40;
- /** @deprecated Use {@link #COLOR_Format24bitRGB888}. */
+ /** @deprecated Use {@link #COLOR_Format24bitBGR888}. */
public static final int COLOR_Format18BitBGR666 = 41;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
+ /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */
public static final int COLOR_Format24BitARGB6666 = 42;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
+ /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */
public static final int COLOR_Format24BitABGR6666 = 43;
/** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
@@ -340,8 +343,22 @@
// In OMX this is called OMX_COLOR_FormatAndroidOpaque.
public static final int COLOR_FormatSurface = 0x7F000789;
- /** @deprecated Use {@link #COLOR_Format32bitARGB8888}. */
- public static final int COLOR_Format32BitRGBA8888 = 0x7F00A000;
+ /**
+ * 32 bits per pixel RGBA color format, with 8-bit red, green, blue, and alpha components.
+ * <p>
+ * Using 32-bit little-endian representation, colors stored as Red 7:0, Green 15:8,
+ * Blue 23:16, and Alpha 31:24.
+ * <pre>
+ * byte byte byte byte
+ * <------ i -----> | <---- i+1 ----> | <---- i+2 ----> | <---- i+3 ----->
+ * +-----------------+-----------------+-----------------+-----------------+
+ * | RED | GREEN | BLUE | ALPHA |
+ * +-----------------+-----------------+-----------------+-----------------+
+ * </pre>
+ *
+ * This corresponds to {@link android.graphics.PixelFormat#RGBA_8888}.
+ */
+ public static final int COLOR_Format32bitABGR8888 = 0x7F00A000;
/**
* Flexible 12 bits per pixel, subsampled YUV color format with 8-bit chroma and luma
@@ -349,24 +366,67 @@
* <p>
* Chroma planes are subsampled by 2 both horizontally and vertically.
* Use this format with {@link Image}.
+ * This format corresponds to {@link android.graphics.ImageFormat#YUV_420_888},
+ * and can represent the {@link #COLOR_FormatYUV411Planar},
+ * {@link #COLOR_FormatYUV411PackedPlanar}, {@link #COLOR_FormatYUV420Planar},
+ * {@link #COLOR_FormatYUV420PackedPlanar}, {@link #COLOR_FormatYUV420SemiPlanar}
+ * and {@link #COLOR_FormatYUV420PackedSemiPlanar} formats.
*
* @see Image#getFormat
*/
- // This corresponds to YUV_420_888 format
public static final int COLOR_FormatYUV420Flexible = 0x7F420888;
/**
* Flexible 16 bits per pixel, subsampled YUV color format with 8-bit chroma and luma
* components.
* <p>
- * Chroma planes are horizontally subsampled by 2.
- * Use this format with {@link Image}.
+ * Chroma planes are horizontally subsampled by 2. Use this format with {@link Image}.
+ * This format corresponds to {@link android.graphics.ImageFormat#YUV_422_888},
+ * and can represent the {@link #COLOR_FormatYCbYCr}, {@link #COLOR_FormatYCrYCb},
+ * {@link #COLOR_FormatCbYCrY}, {@link #COLOR_FormatCrYCbY},
+ * {@link #COLOR_FormatYUV422Planar}, {@link #COLOR_FormatYUV422PackedPlanar},
+ * {@link #COLOR_FormatYUV422SemiPlanar} and {@link #COLOR_FormatYUV422PackedSemiPlanar}
+ * formats.
*
* @see Image#getFormat
*/
- // This corresponds to YUV_422_888 format
public static final int COLOR_FormatYUV422Flexible = 0x7F422888;
+ /**
+ * Flexible 24 bits per pixel YUV color format with 8-bit chroma and luma
+ * components.
+ * <p>
+ * Chroma planes are not subsampled. Use this format with {@link Image}.
+ * This format corresponds to {@link android.graphics.ImageFormat#YUV_444_888},
+ * and can represent the {@link #COLOR_FormatYUV444Interleaved} format.
+ * @see Image#getFormat
+ */
+ public static final int COLOR_FormatYUV444Flexible = 0x7F444888;
+
+ /**
+ * Flexible 24 bits per pixel RGB color format with 8-bit red, green and blue
+ * components.
+ * <p>
+ * Use this format with {@link Image}. This format corresponds to
+ * {@link android.graphics.ImageFormat#FLEX_RGB_888}, and can represent
+ * {@link #COLOR_Format24bitBGR888} and {@link #COLOR_Format24bitRGB888} formats.
+ * @see Image#getFormat.
+ */
+ public static final int COLOR_FormatRGBFlexible = 0x7F36B888;
+
+ /**
+ * Flexible 32 bits per pixel RGBA color format with 8-bit red, green, blue, and alpha
+ * components.
+ * <p>
+ * Use this format with {@link Image}. This format corresponds to
+ * {@link android.graphics.ImageFormat#FLEX_RGBA_8888}, and can represent
+ * {@link #COLOR_Format32bitBGRA8888}, {@link #COLOR_Format32bitABGR8888} and
+ * {@link #COLOR_Format32bitARGB8888} formats.
+ *
+ * @see Image#getFormat
+ */
+ public static final int COLOR_FormatRGBAFlexible = 0x7F36A888;
+
/** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 0x7fa30c00;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 726622f..0e67daa 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -232,11 +232,22 @@
public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
/**
- * @hide
+ * A key describing the stride of the video bytebuffer layout.
+ * Stride (or row increment) is the difference between the index of a pixel
+ * and that of the pixel directly underneath. For YUV 420 formats, the
+ * stride corresponds to the Y plane; the stride of the U and V planes can
+ * be calculated based on the color format.
+ * The associated value is an integer, representing number of bytes.
*/
public static final String KEY_STRIDE = "stride";
+
/**
- * @hide
+ * A key describing the plane height of a multi-planar (YUV) video bytebuffer layout.
+ * Slice height (or plane height) is the number of rows that must be skipped to get
+ * from the top of the Y plane to the top of the U plane in the bytebuffer. In essence
+ * the offset of the U plane is sliceHeight * stride. The height of the U/V planes
+ * can be calculated based on the color format.
+ * The associated value is an integer, representing number of rows.
*/
public static final String KEY_SLICE_HEIGHT = "slice-height";
@@ -456,14 +467,38 @@
/**
* A key describing the desired profile to be used by an encoder.
+ * The associated value is an integer.
* Constants are declared in {@link MediaCodecInfo.CodecProfileLevel}.
- * This key is only supported for codecs that specify a profile.
+ * This key is used as a hint, and is only supported for codecs
+ * that specify a profile.
*
* @see MediaCodecInfo.CodecCapabilities#profileLevels
*/
public static final String KEY_PROFILE = "profile";
/**
+ * A key describing the desired profile to be used by an encoder.
+ * The associated value is an integer.
+ * Constants are declared in {@link MediaCodecInfo.CodecProfileLevel}.
+ * This key is used as a further hint when specifying a desired profile,
+ * and is only supported for codecs that specify a level.
+ * <p>
+ * This key is ignored if the {@link #KEY_PROFILE profile} is not specified.
+ *
+ * @see MediaCodecInfo.CodecCapabilities#profileLevels
+ */
+ public static final String KEY_LEVEL = "level";
+
+ /**
+ * A key describing the desired clockwise rotation on an output surface.
+ * This key is only used when the codec is configured using an output surface.
+ * The associated value is an integer, representing degrees.
+ *
+ * @see MediaCodecInfo.CodecCapabilities#profileLevels
+ */
+ public static final String KEY_ROTATION = "rotation-degrees";
+
+ /**
* A key describing the desired bitrate mode to be used by an encoder.
* Constants are declared in {@link MediaCodecInfo.CodecCapabilities}.
*
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index 541d871..d6bf421 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -43,6 +43,9 @@
private static final String TAG = "MediaHTTPConnection";
private static final boolean VERBOSE = false;
+ // connection timeout - 30 sec
+ private static final int CONNECT_TIMEOUT_MS = 30 * 1000;
+
private long mCurrentOffset = -1;
private URL mURL = null;
private Map<String, String> mHeaders = null;
@@ -182,6 +185,7 @@
} else {
mConnection = (HttpURLConnection)url.openConnection();
}
+ mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
// handle redirects ourselves if we do not allow cross-domain redirect
mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect);
@@ -341,7 +345,7 @@
} catch (UnknownServiceException e) {
Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
return MEDIA_ERROR_UNSUPPORTED;
- }catch (IOException e) {
+ } catch (IOException e) {
if (VERBOSE) {
Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
}
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index c1f1a73..dc6760d 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -29,6 +29,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
import java.util.LinkedList;
import java.util.List;
@@ -48,7 +49,7 @@
* sync.setAudioTrack(audioTrack);
* sync.setCallback(new MediaSync.Callback() {
* {@literal @Override}
- * public void onReturnAudioBuffer(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
+ * public void onAudioBufferConsumed(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
* ...
* }
* }, null);
@@ -87,7 +88,7 @@
* }
*
* // This is the callback from MediaSync.
- * onReturnAudioBuffer(MediaSync sync, ByteBuffer buffer, int bufferIndex) {
+ * onAudioBufferConsumed(MediaSync sync, ByteBuffer buffer, int bufferIndex) {
* // ...
* audioDecoder.releaseBuffer(bufferIndex, false);
* // ...
@@ -103,7 +104,7 @@
* <p>
* For audio, the client needs to set up audio track correctly, e.g., using {@link
* AudioTrack#MODE_STREAM}. The audio buffers are sent to MediaSync directly via {@link
- * #queueAudio}, and are returned to the client via {@link Callback#onReturnAudioBuffer}
+ * #queueAudio}, and are returned to the client via {@link Callback#onAudioBufferConsumed}
* asynchronously. The client should not modify an audio buffer till it's returned.
* <p>
* The client can optionally pre-fill audio/video buffers by setting playback rate to 0.0,
@@ -124,10 +125,41 @@
* @param audioBuffer The returned audio buffer.
* @param bufferIndex The index associated with the audio buffer
*/
- public abstract void onReturnAudioBuffer(
+ public abstract void onAudioBufferConsumed(
@NonNull MediaSync sync, @NonNull ByteBuffer audioBuffer, int bufferIndex);
}
+ /** Audio track failed.
+ * @see android.media.MediaSync.OnErrorListener
+ */
+ public static final int MEDIASYNC_ERROR_AUDIOTRACK_FAIL = 1;
+
+ /** The surface failed to handle video buffers.
+ * @see android.media.MediaSync.OnErrorListener
+ */
+ public static final int MEDIASYNC_ERROR_SURFACE_FAIL = 2;
+
+ /**
+ * Interface definition of a callback to be invoked when there
+ * has been an error during an asynchronous operation (other errors
+ * will throw exceptions at method call time).
+ */
+ public interface OnErrorListener {
+ /**
+ * Called to indicate an error.
+ *
+ * @param sync The MediaSync the error pertains to
+ * @param what The type of error that has occurred:
+ * <ul>
+ * <li>{@link #MEDIASYNC_ERROR_AUDIOTRACK_FAIL}
+ * <li>{@link #MEDIASYNC_ERROR_SURFACE_FAIL}
+ * </ul>
+ * @param extra an extra code, specific to the error. Typically
+ * implementation dependent.
+ */
+ void onError(@NonNull MediaSync sync, int what, int extra);
+ }
+
private static final String TAG = "MediaSync";
private static final int EVENT_CALLBACK = 1;
@@ -154,6 +186,10 @@
private Handler mCallbackHandler = null;
private MediaSync.Callback mCallback = null;
+ private final Object mOnErrorListenerLock = new Object();
+ private Handler mOnErrorListenerHandler = null;
+ private MediaSync.OnErrorListener mOnErrorListener = null;
+
private Thread mAudioThread = null;
// Created on mAudioThread when mAudioThread is started. When used on user thread, they should
// be guarded by checking mAudioThread.
@@ -234,6 +270,39 @@
}
/**
+ * Sets an asynchronous callback for error events.
+ * <p>
+ * This method can be called multiple times to update a previously set listener. If the
+ * handler is changed, undelivered notifications scheduled for the old handler may be dropped.
+ * <p>
+ * <b>Do not call this inside callback.</b>
+ *
+ * @param listener The callback that will run. Use {@code null} to stop receiving callbacks.
+ * @param handler The Handler that will run the callback. Use {@code null} to use MediaSync's
+ * internal handler if it exists.
+ */
+ public void setOnErrorListener(@Nullable /* MediaSync. */ OnErrorListener listener,
+ @Nullable Handler handler) {
+ synchronized(mOnErrorListenerLock) {
+ if (handler != null) {
+ mOnErrorListenerHandler = handler;
+ } else {
+ Looper looper;
+ if ((looper = Looper.myLooper()) == null) {
+ looper = Looper.getMainLooper();
+ }
+ if (looper == null) {
+ mOnErrorListenerHandler = null;
+ } else {
+ mOnErrorListenerHandler = new Handler(looper);
+ }
+ }
+
+ mOnErrorListener = listener;
+ }
+ }
+
+ /**
* Sets the output surface for MediaSync.
* <p>
* Currently, this is only supported in the Initialized state.
@@ -586,8 +655,9 @@
audioBuffer.mSizeInBytes -= sizeWritten;
}
- // TODO: wait time depends on fullness of audio track.
- postRenderAudio(10);
+ long pendingTimeMs = TimeUnit.MICROSECONDS.toMillis(
+ native_getPlayTimeForPendingAudioFrames());
+ postRenderAudio(pendingTimeMs / 2);
}
}
}, delayMillis);
@@ -596,6 +666,8 @@
private native final void native_updateQueuedAudioData(
int sizeInBytes, long presentationTimeUs);
+ private native final long native_getPlayTimeForPendingAudioFrames();
+
private final void postReturnByteBuffer(@NonNull final AudioBuffer audioBuffer) {
synchronized(mCallbackLock) {
if (mCallbackHandler != null) {
@@ -610,7 +682,7 @@
return;
}
if (mCallback != null) {
- mCallback.onReturnAudioBuffer(sync, audioBuffer.mByteBuffer,
+ mCallback.onAudioBufferConsumed(sync, audioBuffer.mByteBuffer,
audioBuffer.mBufferIndex);
}
}
diff --git a/media/java/android/media/OnAudioRecordRoutingListener.java b/media/java/android/media/OnAudioRecordRoutingListener.java
new file mode 100644
index 0000000..8ff41c5
--- /dev/null
+++ b/media/java/android/media/OnAudioRecordRoutingListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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.media;
+
+/**
+ * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
+ * {@link AudioDevicesManager}
+ */
+public interface OnAudioRecordRoutingListener {
+ /**
+ * Called when the routing of an AudioRecord changes from either and explicit or
+ * policy rerouting.
+ */
+ public void onAudioRecordRouting(AudioRecord audioRecord);
+}
diff --git a/media/java/android/media/OnAudioTrackRoutingListener.java b/media/java/android/media/OnAudioTrackRoutingListener.java
new file mode 100644
index 0000000..18c72ef
--- /dev/null
+++ b/media/java/android/media/OnAudioTrackRoutingListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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.media;
+
+/**
+ * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
+ * {@link AudioDevicesManager}
+ */
+public interface OnAudioTrackRoutingListener {
+ /**
+ * Called when the routing of an AudioTrack changes from either and explicit or
+ * policy rerouting.
+ */
+ public void onAudioTrackRouting(AudioTrack audioTrack);
+}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index c539290..69d7028 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;
@@ -32,11 +35,11 @@
* To create a {@code TvContentRating} object, use the
* {@link #createRating TvContentRating.createRating} method with valid rating system string
* constants.
- * <p>
- * It is possible for an application to define its own content rating system by supplying a content
- * rating system definition XML resource (see example below) and declaring a broadcast receiver that
- * filters {@link TvInputManager#ACTION_QUERY_CONTENT_RATING_SYSTEMS} in its manifest.
- * </p>
+ *
+ * <p>It is possible for an application to define its own content rating system by supplying a
+ * content rating system definition XML resource (see example below) and declaring a broadcast
+ * receiver that filters {@link TvInputManager#ACTION_QUERY_CONTENT_RATING_SYSTEMS} in its manifest.
+ *
* <h3> Example: Rating system definition for the TV Parental Guidelines</h3>
* The following XML example shows how the TV Parental Guidelines in the United States can be
* defined:
@@ -120,15 +123,16 @@
* <rating android:name="US_TV_MA" />
* </rating-order>
* </rating-system-definition>
- * </rating-system-definitions>}</pre></p>
+ * </rating-system-definitions>}</pre>
*
* <h3>System defined rating strings</h3>
* The following strings are defined by the system to provide a standard way to create
* {@code TvContentRating} objects.
+ *
* <p>For example, to create an object that represents TV-PG rating with suggestive dialogue and
* coarse language from the TV Parental Guidelines in the United States, one can use the following
* code snippet:
- * </p>
+ *
* <pre>
* TvContentRating rating = TvContentRating.createRating(
* "com.android.tv",
@@ -823,20 +827,17 @@
/**
* Returns {@code true} if this rating has the same main rating as the specified rating and when
* this rating's sub-ratings contain the other's.
- * <p>
- * For example, a {@code TvContentRating} object that represents TV-PG with S(Sexual content)
- * and V(Violence) contains TV-PG, TV-PG/S, TV-PG/V and itself.
- * </p>
+ *
+ * <p>For example, a {@code TvContentRating} object that represents TV-PG with
+ * S(Sexual content) and V(Violence) contains TV-PG, TV-PG/S, TV-PG/V and itself.
*
* @param rating The {@link TvContentRating} to check.
* @return {@code true} if this object contains {@code rating}, {@code false} otherwise.
* @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/TvContract.java b/media/java/android/media/tv/TvContract.java
index 8c61129a..f5a6f2b 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -30,15 +31,13 @@
import java.util.Map;
/**
- * <p>
* The contract between the TV provider and applications. Contains definitions for the supported
* URIs and columns.
- * </p>
* <h3>Overview</h3>
- * <p>
- * TvContract defines a basic database of TV content metadata such as channel and program
+ *
+ * <p>TvContract defines a basic database of TV content metadata such as channel and program
* information. The information is stored in {@link Channels} and {@link Programs} tables.
- * </p>
+ *
* <ul>
* <li>A row in the {@link Channels} table represents information about a TV channel. The data
* format can vary greatly from standard to standard or according to service provider, thus
@@ -156,7 +155,7 @@
* @param inputId The ID of the TV input to build a channels URI for. If {@code null}, builds a
* URI for all the TV inputs.
*/
- public static final Uri buildChannelsUriForInput(String inputId) {
+ public static final Uri buildChannelsUriForInput(@Nullable String inputId) {
return buildChannelsUriForInput(inputId, false);
}
@@ -171,7 +170,8 @@
* @hide
*/
@SystemApi
- public static final Uri buildChannelsUriForInput(String inputId, boolean browsableOnly) {
+ public static final Uri buildChannelsUriForInput(@Nullable String inputId,
+ boolean browsableOnly) {
Uri.Builder builder = Channels.CONTENT_URI.buildUpon();
if (inputId != null) {
builder.appendQueryParameter(PARAM_INPUT, inputId);
@@ -193,8 +193,8 @@
* @hide
*/
@SystemApi
- public static final Uri buildChannelsUriForInput(String inputId, String genre,
- boolean browsableOnly) {
+ public static final Uri buildChannelsUriForInput(@Nullable String inputId,
+ @Nullable String genre, boolean browsableOnly) {
if (genre == null) {
return buildChannelsUriForInput(inputId, browsableOnly);
}
@@ -333,13 +333,12 @@
public interface BaseTvColumns extends BaseColumns {
/**
* The name of the package that owns a row in each table.
- * <p>
- * The TV provider fills it in with the name of the package that provides the initial data
+ *
+ * <p>The TV provider fills it in with the name of the package that provides the initial data
* of that row. If the package is later uninstalled, the rows it owns are automatically
* removed from the tables.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_PACKAGE_NAME = "package_name";
}
@@ -509,181 +508,171 @@
* is not defined for the given video format.
* @see #COLUMN_VIDEO_FORMAT
*/
+ @Nullable
public static final String getVideoResolution(String videoFormat) {
return VIDEO_FORMAT_TO_RESOLUTION_MAP.get(videoFormat);
}
/**
* The ID of the TV input service that provides this TV channel.
- * <p>
- * Use {@link #buildInputId} to build the ID.
- * </p><p>
- * This is a required field.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Use {@link #buildInputId} to build the ID.
+ *
+ * <p>This is a required field.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_INPUT_ID = "input_id";
/**
* The predefined type of this TV channel.
- * <p>
- * This is primarily used to indicate which broadcast standard (e.g. ATSC, DVB or ISDB) the
- * current channel conforms to. The value should match to one of the followings:
+ *
+ * <p>This is primarily used to indicate which broadcast standard (e.g. ATSC, DVB or ISDB)
+ * the current channel conforms to. The value should match to one of the followings:
* {@link #TYPE_OTHER}, {@link #TYPE_DVB_T}, {@link #TYPE_DVB_T2}, {@link #TYPE_DVB_S},
* {@link #TYPE_DVB_S2}, {@link #TYPE_DVB_C}, {@link #TYPE_DVB_C2}, {@link #TYPE_DVB_H},
* {@link #TYPE_DVB_SH}, {@link #TYPE_ATSC_T}, {@link #TYPE_ATSC_C},
* {@link #TYPE_ATSC_M_H}, {@link #TYPE_ISDB_T}, {@link #TYPE_ISDB_TB},
* {@link #TYPE_ISDB_S}, {@link #TYPE_ISDB_C}, {@link #TYPE_1SEG}, {@link #TYPE_DTMB},
* {@link #TYPE_CMMB}, {@link #TYPE_T_DMB}, {@link #TYPE_S_DMB}
- * </p><p>
- * This is a required field.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>This is a required field.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_TYPE = "type";
/**
* The predefined service type of this TV channel.
- * <p>
- * This is primarily used to indicate whether the current channel is a regular TV channel or
- * a radio-like channel. Use the same coding for {@code service_type} in the underlying
+ *
+ * <p>This is primarily used to indicate whether the current channel is a regular TV channel
+ * or a radio-like channel. Use the same coding for {@code service_type} in the underlying
* broadcast standard if it is defined there (e.g. ATSC A/53, ETSI EN 300 468 and ARIB
* STD-B10). Otherwise use one of the followings: {@link #SERVICE_TYPE_OTHER},
* {@link #SERVICE_TYPE_AUDIO_VIDEO}, {@link #SERVICE_TYPE_AUDIO}
- * </p><p>
- * This is a required field.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>This is a required field.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_SERVICE_TYPE = "service_type";
/**
* The original network ID of this TV channel.
- * <p>
- * This is used to identify the originating delivery system, if applicable. Use the same
+ *
+ * <p>This is used to identify the originating delivery system, if applicable. Use the same
* coding for {@code original_network_id} in the underlying broadcast standard if it is
* defined there (e.g. ETSI EN 300 468/TR 101 211 and ARIB STD-B10). If channels cannot be
* globally identified by 2-tuple {{@link #COLUMN_TRANSPORT_STREAM_ID},
* {@link #COLUMN_SERVICE_ID}}, one must carefully assign a value to this field to form a
* unique 3-tuple identification {{@link #COLUMN_ORIGINAL_NETWORK_ID},
* {@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}} for its channels.
- * </p><p>
- * This is a required field if the channel cannot be uniquely identified by a 2-tuple
+ *
+ * <p>This is a required field if the channel cannot be uniquely identified by a 2-tuple
* {{@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}}.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
/**
* The transport stream ID of this channel.
- * <p>
- * This is used to identify the Transport Stream that contains the current channel from any
- * other multiplex within a network, if applicable. Use the same coding for
+ *
+ * <p>This is used to identify the Transport Stream that contains the current channel from
+ * any other multiplex within a network, if applicable. Use the same coding for
* {@code transport_stream_id} defined in ISO/IEC 13818-1 if the channel is transmitted via
* the MPEG Transport Stream as is the case for many digital broadcast standards.
- * </p><p>
- * This is a required field if the current channel is transmitted via the MPEG Transport
+ *
+ * <p>This is a required field if the current channel is transmitted via the MPEG Transport
* Stream.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
/**
* The service ID of this channel.
- * <p>
- * This is used to identify the current service (roughly equivalent to channel) from any
+ *
+ * <p>This is used to identify the current service (roughly equivalent to channel) from any
* other service within the Transport Stream, if applicable. Use the same coding for
* {@code service_id} in the underlying broadcast standard if it is defined there (e.g. ETSI
* EN 300 468 and ARIB STD-B10) or {@code program_number} (which usually has the same value
* as {@code service_id}) in ISO/IEC 13818-1 if the channel is transmitted via the MPEG
* Transport Stream.
- * </p><p>
- * This is a required field if the current channel is transmitted via the MPEG Transport
+ *
+ * <p>This is a required field if the current channel is transmitted via the MPEG Transport
* Stream.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_SERVICE_ID = "service_id";
/**
* The channel number that is displayed to the user.
- * <p>
- * The format can vary depending on broadcast standard and product specification.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>The format can vary depending on broadcast standard and product specification.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_DISPLAY_NUMBER = "display_number";
/**
* The channel name that is displayed to the user.
- * <p>
- * A call sign is a good candidate to use for this purpose but any name that helps the user
- * recognize the current channel will be enough. Can also be empty depending on broadcast
- * standard.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>A call sign is a good candidate to use for this purpose but any name that helps the
+ * user recognize the current channel will be enough. Can also be empty depending on
+ * broadcast standard.
+ *
+ * <p> Type: TEXT
*/
public static final String COLUMN_DISPLAY_NAME = "display_name";
/**
* The network affiliation for this TV channel.
- * <p>
- * This is used to identify a channel that is commonly called by its network affiliation
+ *
+ * <p>This is used to identify a channel that is commonly called by its network affiliation
* instead of the display name. Examples include ABC for the channel KGO-HD, FOX for the
* channel KTVU-HD and NBC for the channel KNTV-HD. Can be empty if not applicable.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
/**
* The description of this TV channel.
- * <p>
- * Can be empty initially.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Can be empty initially.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_DESCRIPTION = "description";
/**
* The typical video format for programs from this TV channel.
- * <p>
- * This is primarily used to filter out channels based on video format by applications. The
- * value should match one of the followings: {@link #VIDEO_FORMAT_240P},
+ *
+ * <p>This is primarily used to filter out channels based on video format by applications.
+ * The value should match one of the followings: {@link #VIDEO_FORMAT_240P},
* {@link #VIDEO_FORMAT_360P}, {@link #VIDEO_FORMAT_480I}, {@link #VIDEO_FORMAT_480P},
* {@link #VIDEO_FORMAT_576I}, {@link #VIDEO_FORMAT_576P}, {@link #VIDEO_FORMAT_720P},
* {@link #VIDEO_FORMAT_1080I}, {@link #VIDEO_FORMAT_1080P}, {@link #VIDEO_FORMAT_2160P},
* {@link #VIDEO_FORMAT_4320P}. Note that the actual video resolution of each program from a
* given channel can vary thus one should use {@link Programs#COLUMN_VIDEO_WIDTH} and
* {@link Programs#COLUMN_VIDEO_HEIGHT} to get more accurate video resolution.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
+ *
* @see #getVideoResolution
*/
public static final String COLUMN_VIDEO_FORMAT = "video_format";
/**
* The flag indicating whether this TV channel is browsable or not.
- * <p>
- * A value of 1 indicates the channel is included in the channel list that applications use
- * to browse channels, a value of 0 indicates the channel is not included in the list. If
- * not specified, this value is set to 0 (not browsable) by default.
- * </p><p>
- * Type: INTEGER (boolean)
- * </p>
+ *
+ * <p>A value of 1 indicates the channel is included in the channel list that applications
+ * use to browse channels, a value of 0 indicates the channel is not included in the list.
+ * If not specified, this value is set to 0 (not browsable) by default.
+ *
+ * <p>Type: INTEGER (boolean)
* @hide
*/
@SystemApi
@@ -691,31 +680,29 @@
/**
* The flag indicating whether this TV channel is searchable or not.
- * <p>
- * In some regions, it is not allowed to surface search results for a given channel without
- * broadcaster's consent. This is used to impose such restriction. Channels marked with
- * "not searchable" cannot be used by other services except for the system service that
+ *
+ * <p>In some regions, it is not allowed to surface search results for a given channel
+ * without broadcaster's consent. This is used to impose such restriction. Channels marked
+ * with "not searchable" cannot be used by other services except for the system service that
* shows the TV content. A value of 1 indicates the channel is searchable and can be
* included in search results, a value of 0 indicates the channel and its TV programs are
* hidden from search. If not specified, this value is set to 1 (searchable) by default.
- * </p><p>
- * Type: INTEGER (boolean)
- * </p>
+ *
+ * <p>Type: INTEGER (boolean)
*/
public static final String COLUMN_SEARCHABLE = "searchable";
/**
* The flag indicating whether this TV channel is locked or not.
- * <p>
- * This is primarily used for alternative parental control to prevent unauthorized users
+ *
+ * <p>This is primarily used for alternative parental control to prevent unauthorized users
* from watching the current channel regardless of the content rating. A value of 1
* indicates the channel is locked and the user is required to enter passcode to unlock it
* in order to watch the current program from the channel, a value of 0 indicates the
* channel is not locked thus the user is not prompted to enter passcode If not specified,
* this value is set to 0 (not locked) by default.
- * </p><p>
- * Type: INTEGER (boolean)
- * </p>
+ *
+ * <p>Type: INTEGER (boolean)
* @hide
*/
@SystemApi
@@ -723,69 +710,63 @@
/**
* Internal data used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: BLOB
- * </p>
+ *
+ * <p>Type: BLOB
*/
public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
/**
* Internal integer flag used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
/**
* Internal integer flag used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
/**
* Internal integer flag used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
/**
* Internal integer flag used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
/**
* The version number of this row entry used by TV input services.
- * <p>
- * This is best used by sync adapters to identify the rows to update. The number can be
+ *
+ * <p>This is best used by sync adapters to identify the rows to update. The number can be
* defined by individual TV input services. One may assign the same value as
* {@code version_number} that appears in ETSI EN 300 468 or ATSC A/65, if the data are
* coming from a TV broadcast.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_VERSION_NUMBER = "version_number";
@@ -793,18 +774,18 @@
/**
* A sub-directory of a single TV channel that represents its primary logo.
- * <p>
- * To access this directory, append {@link Channels.Logo#CONTENT_DIRECTORY} to the raw
+ *
+ * <p>To access this directory, append {@link Channels.Logo#CONTENT_DIRECTORY} to the raw
* channel URI. The resulting URI represents an image file, and should be interacted
* using ContentResolver.openAssetFileDescriptor.
- * </p><p>
- * Note that this sub-directory also supports opening the logo as an asset file in write
+ *
+ * <p>Note that this sub-directory also supports opening the logo as an asset file in write
* mode. Callers can create or replace the primary logo associated with this channel by
* opening the asset file and writing the full-size photo contents into it. (Make sure there
* is no padding around the logo image.) When the file is closed, the image will be parsed,
* sized down if necessary, and stored.
- * </p><p>
- * Usage example:
+ *
+ * <p>Usage example:
* <pre>
* public void writeChannelLogo(long channelId, byte[] logo) {
* Uri channelLogoUri = TvContract.buildChannelLogoUri(channelId);
@@ -820,7 +801,6 @@
* }
* }
* </pre>
- * </p>
*/
public static final class Logo {
@@ -835,10 +815,9 @@
/**
* Column definitions for the TV programs table.
- * <p>
- * By default, the query results will be sorted by {@link Programs#COLUMN_START_TIME_UTC_MILLIS}
- * in ascending order.
- * </p>
+ *
+ * <p>By default, the query results will be sorted by
+ * {@link Programs#COLUMN_START_TIME_UTC_MILLIS} in ascending order.
*/
public static final class Programs implements BaseTvColumns {
@@ -854,166 +833,153 @@
/**
* The ID of the TV channel that provides this TV program.
- * <p>
- * This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
- * </p><p>
- * Type: INTEGER (long)
- * </p>
+ *
+ * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
+ *
+ * <p>Type: INTEGER (long)
*/
public static final String COLUMN_CHANNEL_ID = "channel_id";
/**
* The title of this TV program.
- * <p>
- * If this program is an episodic TV show, it is recommended that the title is the series
+ *
+ * <p>If this program is an episodic TV show, it is recommended that the title is the series
* title and its related fields ({@link #COLUMN_SEASON_NUMBER},
* {@link #COLUMN_EPISODE_NUMBER}, and {@link #COLUMN_EPISODE_TITLE}) are filled in.
- * </p><p>
- * Type: TEXT
- * </p>
- **/
+ *
+ * <p>Type: TEXT
+ */
public static final String COLUMN_TITLE = "title";
/**
* The season number of this TV program for episodic TV shows.
- * <p>
- * Can be empty.
- * </p><p>
- * Type: INTEGER
- * </p>
- **/
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: INTEGER
+ */
public static final String COLUMN_SEASON_NUMBER = "season_number";
/**
* The episode number of this TV program for episodic TV shows.
- * <p>
- * Can be empty.
- * </p><p>
- * Type: INTEGER
- * </p>
- **/
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: INTEGER
+ */
public static final String COLUMN_EPISODE_NUMBER = "episode_number";
/**
* The episode title of this TV program for episodic TV shows.
- * <p>
- * Can be empty.
- * </p><p>
- * Type: TEXT
- * </p>
- **/
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
public static final String COLUMN_EPISODE_TITLE = "episode_title";
/**
* The start time of this TV program, in milliseconds since the epoch.
- * <p>
- * The value should be equal to or larger than {@link #COLUMN_END_TIME_UTC_MILLIS} of the
+ *
+ * <p>The value should be equal to or larger than {@link #COLUMN_END_TIME_UTC_MILLIS} of the
* previous program in the same channel.
- * </p><p>
- * Type: INTEGER (long)
- * </p>
+ *
+ * <p>Type: INTEGER (long)
*/
public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
/**
* The end time of this TV program, in milliseconds since the epoch.
- * <p>
- * The value should be equal to or less than {@link #COLUMN_START_TIME_UTC_MILLIS} of the
+ *
+ * <p>The value should be equal to or less than {@link #COLUMN_START_TIME_UTC_MILLIS} of the
* next program in the same channel.
- * </p><p>
- * Type: INTEGER (long)
- * </p>
+ *
+ * <p>Type: INTEGER (long)
*/
public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
/**
* The comma-separated genre string of this TV program.
- * <p>
- * Use the same language appeared in the underlying broadcast standard, if applicable. (For
- * example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+ *
+ * <p>Use the same language appeared in the underlying broadcast standard, if applicable.
+ * (For example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
* Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, leave empty.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_BROADCAST_GENRE = "broadcast_genre";
/**
* The comma-separated canonical genre string of this TV program.
- * <p>
- * Canonical genres are defined in {@link Genres}. Use {@link Genres#encode Genres.encode()}
- * to create a text that can be stored in this column. Use {@link Genres#decode
- * Genres.decode()} to get the canonical genre strings from the text stored in this column.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Canonical genres are defined in {@link Genres}. Use
+ * {@link Genres#encode Genres.encode()} to create a text that can be stored in this column.
+ * Use {@link Genres#decode Genres.decode()} to get the canonical genre strings from the
+ * text stored in this column.
+ *
+ * <p>Type: TEXT
* @see Genres
*/
public static final String COLUMN_CANONICAL_GENRE = "canonical_genre";
/**
* The short description of this TV program that is displayed to the user by default.
- * <p>
- * It is recommended to limit the length of the descriptions to 256 characters.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>It is recommended to limit the length of the descriptions to 256 characters.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
/**
* The detailed, lengthy description of this TV program that is displayed only when the user
* wants to see more information.
- * <p>
- * TV input services should leave this field empty if they have no additional details beyond
- * {@link #COLUMN_SHORT_DESCRIPTION}.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>TV input services should leave this field empty if they have no additional details
+ * beyond {@link #COLUMN_SHORT_DESCRIPTION}.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_LONG_DESCRIPTION = "long_description";
/**
* The width of the video for this TV program, in the unit of pixels.
- * <p>
- * Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video resolution
- * of the current TV program. Can be empty if it is not known initially or the program does
- * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO}
- * channels.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video
+ * resolution of the current TV program. Can be empty if it is not known initially or the
+ * program does not convey any video such as the programs from type
+ * {@link Channels#SERVICE_TYPE_AUDIO} channels.
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_VIDEO_WIDTH = "video_width";
/**
* The height of the video for this TV program, in the unit of pixels.
- * <p>
- * Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video resolution
- * of the current TV program. Can be empty if it is not known initially or the program does
- * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO}
- * channels.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video
+ * resolution of the current TV program. Can be empty if it is not known initially or the
+ * program does not convey any video such as the programs from type
+ * {@link Channels#SERVICE_TYPE_AUDIO} channels.
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_VIDEO_HEIGHT = "video_height";
/**
* The comma-separated audio languages of this TV program.
- * <p>
- * This is used to describe available audio languages included in the program. Use either
+ *
+ * <p>This is used to describe available audio languages included in the program. Use either
* ISO 639-1 or 639-2/T codes.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
/**
* The comma-separated content ratings of this TV program.
- * <p>
- * This is used to describe the content rating(s) of this program. Each comma-separated
+ *
+ * <p>This is used to describe the content rating(s) of this program. Each comma-separated
* content rating sub-string should be generated by calling
* {@link TvContentRating#flattenToString}. Note that in most cases the program content is
* rated by a single rating system, thus resulting in a corresponding single sub-string that
@@ -1022,97 +988,88 @@
* specified as "blocked rating" in the user's parental control settings, the TV input
* service should block the current content and wait for the signal that it is okay to
* unblock.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_CONTENT_RATING = "content_rating";
/**
* The URI for the poster art of this TV program.
- * <p>
- * Can be empty.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
/**
* The URI for the thumbnail of this TV program.
- * <p>
- * Can be empty.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
/**
* Internal data used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: BLOB
- * </p>
+ *
+ * <p>Type: BLOB
*/
public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
/**
* Internal integer flag used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
/**
* Internal integer flag used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
/**
* Internal integer flag used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
/**
* Internal integer flag used by individual TV input services.
- * <p>
- * This is internal to the provider that inserted it, and should not be decoded by other
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
* apps.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
/**
* The version number of this row entry used by TV input services.
- * <p>
- * This is best used by sync adapters to identify the rows to update. The number can be
+ *
+ * <p>This is best used by sync adapters to identify the rows to update. The number can be
* defined by individual TV input services. One may assign the same value as
* {@code version_number} in ETSI EN 300 468 or ATSC A/65, if the data are coming from a TV
* broadcast.
- * </p><p>
- * Type: INTEGER
- * </p>
+ *
+ * <p>Type: INTEGER
*/
public static final String COLUMN_VERSION_NUMBER = "version_number";
@@ -1239,10 +1196,9 @@
/**
* Column definitions for the TV programs that the user watched. Applications do not have access
* to this table.
- * <p>
- * By default, the query results will be sorted by
+ *
+ * <p>By default, the query results will be sorted by
* {@link WatchedPrograms#COLUMN_WATCH_START_TIME_UTC_MILLIS} in descending order.
- * </p>
* @hide
*/
@SystemApi
@@ -1261,9 +1217,8 @@
/**
* The UTC time that the user started watching this TV program, in milliseconds since the
* epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
+ *
+ * <p>Type: INTEGER (long)
*/
public static final String COLUMN_WATCH_START_TIME_UTC_MILLIS =
"watch_start_time_utc_millis";
@@ -1271,49 +1226,43 @@
/**
* The UTC time that the user stopped watching this TV program, in milliseconds since the
* epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
+ *
+ * <p>Type: INTEGER (long)
*/
public static final String COLUMN_WATCH_END_TIME_UTC_MILLIS = "watch_end_time_utc_millis";
/**
* The ID of the TV channel that provides this TV program.
- * <p>
- * Type: INTEGER (long)
- * </p>
+ *
+ * <p>Type: INTEGER (long)
*/
public static final String COLUMN_CHANNEL_ID = "channel_id";
/**
* The title of this TV program.
- * <p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_TITLE = "title";
/**
* The start time of this TV program, in milliseconds since the epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
+ *
+ * <p>Type: INTEGER (long)
*/
public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
/**
* The end time of this TV program, in milliseconds since the epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
+ *
+ * <p>Type: INTEGER (long)
*/
public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
/**
* The description of this TV program.
- * <p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_DESCRIPTION = "description";
@@ -1321,25 +1270,23 @@
* Extra parameters given to {@link TvInputService.Session#tune(Uri, android.os.Bundle)
* TvInputService.Session.tune(Uri, android.os.Bundle)} when tuning to the channel that
* provides this TV program. (Used internally.)
- * <p>
- * This column contains an encoded string that represents comma-separated key-value pairs of
+ *
+ * <p>This column contains an encoded string that represents comma-separated key-value pairs of
* the tune parameters. (Ex. "[key1]=[value1], [key2]=[value2]"). '%' is used as an escape
* character for '%', '=', and ','.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_INTERNAL_TUNE_PARAMS = "tune_params";
/**
* The session token of this TV program. (Used internally.)
- * <p>
- * This contains a String representation of {@link IBinder} for
+ *
+ * <p>This contains a String representation of {@link IBinder} for
* {@link TvInputService.Session} that provides the current TV program. It is used
* internally to distinguish watched programs entries from different TV input sessions.
- * </p><p>
- * Type: TEXT
- * </p>
+ *
+ * <p>Type: TEXT
*/
public static final String COLUMN_INTERNAL_SESSION_TOKEN = "session_token";
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 5c1193f..46d33b4 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
@@ -295,18 +296,17 @@
/**
* Returns the parent input ID.
- * <p>
- * A TV input may have a parent input if the TV input is actually a logical representation of
+ *
+ * <p>A TV input may have a parent input if the TV input is actually a logical representation of
* a device behind the hardware port represented by the parent input.
* For example, a HDMI CEC logical device, connected to a HDMI port, appears as another TV
* input. In this case, the parent input of this logical device is the HDMI port.
- * </p><p>
- * Applications may group inputs by parent input ID to provide an easier access to inputs
+ *
+ * <p>Applications may group inputs by parent input ID to provide an easier access to inputs
* sharing the same physical port. In the example of HDMI CEC, logical HDMI CEC devices behind
* the same HDMI port have the same parent ID, which is the ID representing the port. Thus
* applications can group the hardware HDMI port and the logical HDMI CEC devices behind it
* together using this method.
- * </p>
*
* @return the ID of the parent input, if exists. Returns {@code null} if the parent input is
* not specified.
@@ -425,7 +425,7 @@
* @return a CharSequence containing the TV input's label. If the TV input does not have
* a label, its name is returned.
*/
- public CharSequence loadLabel(Context context) {
+ public CharSequence loadLabel(@NonNull Context context) {
if (TextUtils.isEmpty(mLabel)) {
return mService.loadLabel(context.getPackageManager());
} else {
@@ -453,7 +453,7 @@
* @return a Drawable containing the TV input's icon. If the TV input does not have an icon,
* application's icon is returned. If it's unavailable too, {@code null} is returned.
*/
- public Drawable loadIcon(Context context) {
+ public Drawable loadIcon(@NonNull Context context) {
if (mIconUri == null) {
return loadServiceIcon(context);
}
@@ -507,7 +507,7 @@
* @param flags The flags used for parceling.
*/
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
dest.writeString(mParentId);
mService.writeToParcel(dest, flags);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 99d254e..601fa45 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -16,6 +16,8 @@
package android.media.tv;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.Rect;
import android.media.MediaPlayer;
@@ -38,6 +40,8 @@
import android.view.Surface;
import android.view.View;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
@@ -149,15 +153,17 @@
/**
* Broadcast intent action used to query available content rating systems.
- * <p>
- * The TV input manager service locates available content rating systems by querying broadcast
- * receivers that are registered for this action. An application can offer additional content
- * rating systems to the user by declaring a suitable broadcast receiver in its manifest.
- * </p><p>
- * Here is an example broadcast receiver declaration that an application might include in its
+ *
+ * <p>The TV input manager service locates available content rating systems by querying
+ * broadcast receivers that are registered for this action. An application can offer additional
+ * content rating systems to the user by declaring a suitable broadcast receiver in its
+ * manifest.
+ *
+ * <p>Here is an example broadcast receiver declaration that an application might include in its
* AndroidManifest.xml to advertise custom content rating systems. The meta-data specifies a
* resource that contains a description of each content rating system that is provided by the
* application.
+ *
* <p><pre class="prettyprint">
* {@literal
* <receiver android:name=".TvInputReceiver">
@@ -168,13 +174,13 @@
* <meta-data
* android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS"
* android:resource="@xml/tv_content_rating_systems" />
- * </receiver>}</pre></p>
- * In the above example, the <code>@xml/tv_content_rating_systems</code> resource refers to an
+ * </receiver>}</pre>
+ *
+ * <p>In the above example, the <code>@xml/tv_content_rating_systems</code> resource refers to an
* XML resource whose root element is <code><rating-system-definitions></code> that
* contains zero or more <code><rating-system-definition></code> elements. Each <code>
* <rating-system-definition></code> element specifies the ratings, sub-ratings and rating
* orders of a particular content rating system.
- * </p>
*
* @see TvContentRating
*/
@@ -183,10 +189,9 @@
/**
* Content rating systems metadata associated with {@link #ACTION_QUERY_CONTENT_RATING_SYSTEMS}.
- * <p>
- * Specifies the resource ID of an XML resource that describes the content rating systems that
- * are provided by the application.
- * </p>
+ *
+ * <p>Specifies the resource ID of an XML resource that describes the content rating systems
+ * that are provided by the application.
*/
public static final String META_DATA_CONTENT_RATING_SYSTEMS =
"android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
@@ -229,7 +234,7 @@
* @param session A {@link TvInputManager.Session} instance created. This can be
* {@code null} if the creation request failed.
*/
- public void onSessionCreated(Session session) {
+ public void onSessionCreated(@Nullable Session session) {
}
/**
@@ -270,7 +275,7 @@
* @param trackId The ID of the selected track. When {@code null} the currently selected
* track for a given type should be unselected.
*/
- public void onTrackSelected(Session session, int type, String trackId) {
+ public void onTrackSelected(Session session, int type, @Nullable String trackId) {
}
/**
@@ -371,12 +376,11 @@
/**
* This is called when the start playback position is changed.
- * <p>
- * The start playback position of the time shifted program should be adjusted when the TV
+ *
+ * <p>The start playback position of the time shifted program should be adjusted when the TV
* input cannot retain the whole recorded program due to some reason (e.g. limitation on
* storage space). This is necessary to prevent the application from allowing the user to
* seek to a time position that is not reachable.
- * </p>
*
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param timeMs The start playback position of the time shifted program, in milliseconds
@@ -930,10 +934,9 @@
* @param inputId The ID of the TV input.
* @return the {@link TvInputInfo} for a given TV input. {@code null} if not found.
*/
- public TvInputInfo getTvInputInfo(String inputId) {
- if (inputId == null) {
- throw new IllegalArgumentException("inputId cannot be null");
- }
+ @Nullable
+ public TvInputInfo getTvInputInfo(@NonNull String inputId) {
+ Preconditions.checkNotNull(inputId);
try {
return mService.getTvInputInfo(inputId, mUserId);
} catch (RemoteException e) {
@@ -954,10 +957,8 @@
* @param inputId The id of the TV input.
* @throws IllegalArgumentException if the argument is {@code null}.
*/
- public int getInputState(String inputId) {
- if (inputId == null) {
- throw new IllegalArgumentException("inputId cannot be null");
- }
+ public int getInputState(@NonNull String inputId) {
+ Preconditions.checkNotNull(inputId);
synchronized (mLock) {
Integer state = mStateMap.get(inputId);
if (state == null) {
@@ -973,15 +974,10 @@
*
* @param callback A callback used to monitor status of the TV inputs.
* @param handler A {@link Handler} that the status change will be delivered to.
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void registerCallback(TvInputCallback callback, Handler handler) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- if (handler == null) {
- throw new IllegalArgumentException("handler cannot be null");
- }
+ public void registerCallback(@NonNull TvInputCallback callback, @NonNull Handler handler) {
+ Preconditions.checkNotNull(callback);
+ Preconditions.checkNotNull(handler);
synchronized (mLock) {
mCallbackRecords.add(new TvInputCallbackRecord(callback, handler));
}
@@ -991,12 +987,9 @@
* Unregisters the existing {@link TvInputCallback}.
*
* @param callback The existing callback to remove.
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void unregisterCallback(final TvInputCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
+ public void unregisterCallback(@NonNull final TvInputCallback callback) {
+ Preconditions.checkNotNull(callback);
synchronized (mLock) {
for (Iterator<TvInputCallbackRecord> it = mCallbackRecords.iterator();
it.hasNext(); ) {
@@ -1045,10 +1038,8 @@
* @param rating The TV content rating to check.
* @return {@code true} if the given TV content rating is blocked, {@code false} otherwise.
*/
- public boolean isRatingBlocked(TvContentRating rating) {
- if (rating == null) {
- throw new IllegalArgumentException("rating cannot be null");
- }
+ public boolean isRatingBlocked(@NonNull TvContentRating rating) {
+ Preconditions.checkNotNull(rating);
try {
return mService.isRatingBlocked(rating.flattenToString(), mUserId);
} catch (RemoteException e) {
@@ -1084,10 +1075,8 @@
* @hide
*/
@SystemApi
- public void addBlockedRating(TvContentRating rating) {
- if (rating == null) {
- throw new IllegalArgumentException("rating cannot be null");
- }
+ public void addBlockedRating(@NonNull TvContentRating rating) {
+ Preconditions.checkNotNull(rating);
try {
mService.addBlockedRating(rating.flattenToString(), mUserId);
} catch (RemoteException e) {
@@ -1104,10 +1093,8 @@
* @hide
*/
@SystemApi
- public void removeBlockedRating(TvContentRating rating) {
- if (rating == null) {
- throw new IllegalArgumentException("rating cannot be null");
- }
+ public void removeBlockedRating(@NonNull TvContentRating rating) {
+ Preconditions.checkNotNull(rating);
try {
mService.removeBlockedRating(rating.flattenToString(), mUserId);
} catch (RemoteException e) {
@@ -1130,29 +1117,21 @@
/**
* Creates a {@link Session} for a given TV input.
- * <p>
- * The number of sessions that can be created at the same time is limited by the capability of
- * the given TV input.
- * </p>
+ *
+ * <p>The number of sessions that can be created at the same time is limited by the capability
+ * of the given TV input.
*
* @param inputId The id of the TV input.
* @param callback A callback used to receive the created session.
* @param handler A {@link Handler} that the session creation will be delivered to.
- * @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++;
@@ -1434,7 +1413,6 @@
* Tunes to a given channel.
*
* @param channelUri The URI of a channel.
- * @throws IllegalArgumentException if the argument is {@code null}.
*/
public void tune(Uri channelUri) {
tune(channelUri, null);
@@ -1445,14 +1423,11 @@
*
* @param channelUri The URI of a channel.
* @param params A set of extra parameters which might be handled with this tune event.
- * @throws IllegalArgumentException if {@code channelUri} is {@code null}.
* @hide
*/
@SystemApi
- public void tune(Uri channelUri, Bundle params) {
- if (channelUri == null) {
- throw new IllegalArgumentException("channelUri cannot be null");
- }
+ public void tune(@NonNull Uri channelUri, Bundle params) {
+ Preconditions.checkNotNull(channelUri);
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
@@ -1501,7 +1476,7 @@
* track of the given type will be unselected.
* @see #getTracks
*/
- public void selectTrack(int type, String trackId) {
+ public void selectTrack(int type, @Nullable String trackId) {
synchronized (mMetadataLock) {
if (type == TvTrackInfo.TYPE_AUDIO) {
if (trackId != null && !containsTrack(mAudioTracks, trackId)) {
@@ -1550,6 +1525,7 @@
* {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
* @return the list of tracks for the given type.
*/
+ @Nullable
public List<TvTrackInfo> getTracks(int type) {
synchronized (mMetadataLock) {
if (type == TvTrackInfo.TYPE_AUDIO) {
@@ -1579,6 +1555,7 @@
* @return the ID of the selected track.
* @see #selectTrack
*/
+ @Nullable
public String getSelectedTrack(int type) {
synchronized (mMetadataLock) {
if (type == TvTrackInfo.TYPE_AUDIO) {
@@ -1694,8 +1671,8 @@
/**
* Seeks to a specified time position.
- * <p>
- * Normally, the position is given within range between the start and the current time,
+ *
+ * <p>Normally, the position is given within range between the start and the current time,
* inclusively.
*
* @param timeMs The time position to seek to, in milliseconds since the epoch.
@@ -1786,16 +1763,11 @@
*
* @param view A view playing TV.
* @param frame A position of the overlay view.
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
* @throws IllegalStateException if {@code view} is not attached to a window.
*/
- void createOverlayView(View view, Rect frame) {
- if (view == null) {
- throw new IllegalArgumentException("view cannot be null");
- }
- if (frame == null) {
- throw new IllegalArgumentException("frame cannot be null");
- }
+ void createOverlayView(@NonNull View view, @NonNull Rect frame) {
+ Preconditions.checkNotNull(view);
+ Preconditions.checkNotNull(frame);
if (view.getWindowToken() == null) {
throw new IllegalStateException("view must be attached to a window");
}
@@ -1814,12 +1786,9 @@
* Relayouts the current overlay view.
*
* @param frame A new position of the overlay view.
- * @throws IllegalArgumentException if the arguments is {@code null}.
*/
- void relayoutOverlayView(Rect frame) {
- if (frame == null) {
- throw new IllegalArgumentException("frame cannot be null");
- }
+ void relayoutOverlayView(@NonNull Rect frame) {
+ Preconditions.checkNotNull(frame);
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
@@ -1849,14 +1818,12 @@
/**
* Requests to unblock content blocked by parental controls.
*/
- void requestUnblockContent(TvContentRating unblockedRating) {
+ void requestUnblockContent(@NonNull TvContentRating unblockedRating) {
+ Preconditions.checkNotNull(unblockedRating);
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
- if (unblockedRating == null) {
- throw new IllegalArgumentException("unblockedRating cannot be null");
- }
try {
mService.requestUnblockContent(mToken, unblockedRating.flattenToString(), mUserId);
} catch (RemoteException e) {
@@ -1867,25 +1834,22 @@
/**
* Dispatches an input event to this session.
*
- * @param event An {@link InputEvent} to dispatch.
+ * @param event An {@link InputEvent} to dispatch. Cannot be {@code null}.
* @param token A token used to identify the input event later in the callback.
- * @param callback A callback used to receive the dispatch result.
- * @param handler A {@link Handler} that the dispatch result will be delivered to.
+ * @param callback A callback used to receive the dispatch result. Cannot be {@code null}.
+ * @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be
+ * {@code null}.
* @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
* {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
* {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
* be invoked later.
- * @throws IllegalArgumentException if any of the necessary arguments is {@code null}.
* @hide
*/
- public int dispatchInputEvent(InputEvent event, Object token,
- FinishedInputEventCallback callback, Handler handler) {
- if (event == null) {
- throw new IllegalArgumentException("event cannot be null");
- }
- if (callback != null && handler == null) {
- throw new IllegalArgumentException("handler cannot be null");
- }
+ public int dispatchInputEvent(@NonNull InputEvent event, Object token,
+ @NonNull FinishedInputEventCallback callback, @NonNull Handler handler) {
+ Preconditions.checkNotNull(event);
+ Preconditions.checkNotNull(callback);
+ Preconditions.checkNotNull(handler);
synchronized (mHandler) {
if (mChannel == null) {
return DISPATCH_NOT_HANDLED;
@@ -2113,7 +2077,7 @@
/**
* The Hardware provides the per-hardware functionality of TV hardware.
*
- * TV hardware is physical hardware attached to the Android device; for example, HDMI ports,
+ * <p>TV hardware is physical hardware attached to the Android device; for example, HDMI ports,
* Component/Composite ports, etc. Specifically, logical devices such as HDMI CEC logical
* devices don't fall into this category.
*
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 278d627..5156ae8 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,8 @@
package android.media.tv;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
@@ -49,6 +51,7 @@
import android.widget.FrameLayout;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.HashSet;
@@ -58,15 +61,14 @@
/**
* The TvInputService class represents a TV input or source such as HDMI or built-in tuner which
* provides pass-through video or broadcast TV programs.
- * <p>
- * Applications will not normally use this service themselves, instead relying on the standard
+ *
+ * <p>Applications will not normally use this service themselves, instead relying on the standard
* interaction provided by {@link TvView}. Those implementing TV input services should normally do
* so by deriving from this class and providing their own session implementation based on
* {@link TvInputService.Session}. All TV input services must require that clients hold the
* {@link android.Manifest.permission#BIND_TV_INPUT} in order to interact with the service; if this
* permission is not specified in the manifest, the system will refuse to bind to that TV input
* service.
- * </p>
*/
public abstract class TvInputService extends Service {
private static final boolean DEBUG = false;
@@ -159,13 +161,14 @@
/**
* Returns a concrete implementation of {@link Session}.
- * <p>
- * May return {@code null} if this TV input service fails to create a session for some reason.
- * If TV input represents an external device connected to a hardware TV input,
+ *
+ * <p>May return {@code null} if this TV input service fails to create a session for some
+ * reason. If TV input represents an external device connected to a hardware TV input,
* {@link HardwareSession} should be returned.
- * </p>
+ *
* @param inputId The ID of the TV input associated with the session.
*/
+ @Nullable
public abstract Session onCreateSession(String inputId);
/**
@@ -176,6 +179,7 @@
* @param hardwareInfo {@link TvInputHardwareInfo} object just added.
* @hide
*/
+ @Nullable
@SystemApi
public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) {
return null;
@@ -189,6 +193,7 @@
* @param hardwareInfo {@link TvInputHardwareInfo} object just removed.
* @hide
*/
+ @Nullable
@SystemApi
public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
return null;
@@ -202,6 +207,7 @@
* @param deviceInfo {@link HdmiDeviceInfo} object just added.
* @hide
*/
+ @Nullable
@SystemApi
public TvInputInfo onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
return null;
@@ -215,6 +221,7 @@
* @param deviceInfo {@link HdmiDeviceInfo} object just removed.
* @hide
*/
+ @Nullable
@SystemApi
public String onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
return null;
@@ -307,10 +314,8 @@
* @hide
*/
@SystemApi
- public void notifySessionEvent(final String eventType, final Bundle eventArgs) {
- if (eventType == null) {
- throw new IllegalArgumentException("eventType should not be null.");
- }
+ public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) {
+ Preconditions.checkNotNull(eventType);
executeOrPostRunnable(new Runnable() {
@Override
public void run() {
@@ -476,8 +481,8 @@
/**
* Informs the application that the user is allowed to watch the current program content.
- * <p>
- * Each TV input service is required to query the system whether the user is allowed to
+ *
+ * <p>Each TV input service is required to query the system whether the user is allowed to
* watch the current program before showing it to the user if the parental controls is
* enabled (i.e. {@link TvInputManager#isParentalControlsEnabled
* TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input
@@ -488,13 +493,12 @@
* result. If the rating in question turns out to be allowed by the user, the TV input
* service must call this method to notify the application that is permitted to show the
* content.
- * </p><p>
- * Each TV input service also needs to continuously listen to any changes made to the
+ *
+ * <p>Each TV input service also needs to continuously listen to any changes made to the
* parental controls settings by registering a broadcast receiver to receive
* {@link TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} and
* {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately
* reevaluate the current program with the new parental controls settings.
- * </p>
*
* @see #notifyContentBlocked
* @see TvInputManager
@@ -517,8 +521,8 @@
/**
* Informs the application that the current program content is blocked by parent controls.
- * <p>
- * Each TV input service is required to query the system whether the user is allowed to
+ *
+ * <p>Each TV input service is required to query the system whether the user is allowed to
* watch the current program before showing it to the user if the parental controls is
* enabled (i.e. {@link TvInputManager#isParentalControlsEnabled
* TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input
@@ -529,19 +533,19 @@
* result. If the rating in question turns out to be blocked, the TV input service must
* immediately block the content and call this method with the content rating of the current
* program to prompt the PIN verification screen.
- * </p><p>
- * Each TV input service also needs to continuously listen to any changes made to the
+ *
+ * <p>Each TV input service also needs to continuously listen to any changes made to the
* parental controls settings by registering a broadcast receiver to receive
* {@link TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} and
* {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately
* reevaluate the current program with the new parental controls settings.
- * </p>
*
* @param rating The content rating for the current TV program.
* @see #notifyContentAllowed
* @see TvInputManager
*/
- public void notifyContentBlocked(final TvContentRating rating) {
+ public void notifyContentBlocked(@NonNull final TvContentRating rating) {
+ Preconditions.checkNotNull(rating);
executeOrPostRunnable(new Runnable() {
@Override
public void run() {
@@ -559,22 +563,21 @@
/**
* Informs the application that the time shift status is changed.
- * <p>
- * Prior to calling this method, the application assumes the status
+ *
+ * <p>Prior to calling this method, the application assumes the status
* {@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN}. Right after the session is created, it
* is important to invoke the method with the status
* {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} if the implementation does support
* time shifting, or {@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED} otherwise. Failure
* to notifying the current status change immediately might result in an undesirable
* behavior in the application such as hiding the play controls.
- * </p><p>
- * If the status {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} is reported, the
+ *
+ * <p>If the status {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} is reported, the
* application assumes it can pause/resume playback, seek to a specified time position and
* set playback rate and audio mode. The implementation should override
* {@link #onTimeShiftPause}, {@link #onTimeShiftResume}, {@link #onTimeShiftSeekTo},
* {@link #onTimeShiftGetStartPosition}, {@link #onTimeShiftGetCurrentPosition} and
* {@link #onTimeShiftSetPlaybackRate}.
- * </p>
*
* @param status The current time shift status. Should be one of the followings.
* <ul>
@@ -672,21 +675,20 @@
/**
* Sets the current session as the main session. The main session is a session whose
* corresponding TV input determines the HDMI-CEC active source device.
- * <p>
- * TV input service that manages HDMI-CEC logical device should implement {@link
+ *
+ * <p>TV input service that manages HDMI-CEC logical device should implement {@link
* #onSetMain} to (1) select the corresponding HDMI logical device as the source device
* when {@code isMain} is {@code true}, and to (2) select the internal device (= TV itself)
* as the source device when {@code isMain} is {@code false} and the session is still main.
* Also, if a surface is passed to a non-main session and active source is changed to
* initiate the surface, the active source should be returned to the main session.
- * </p><p>
- * {@link TvView} guarantees that, when tuning involves a session transition, {@code
+ *
+ * <p>{@link TvView} guarantees that, when tuning involves a session transition, {@code
* onSetMain(true)} for new session is called first, {@code onSetMain(false)} for old
* session is called afterwards. This allows {@code onSetMain(false)} to be no-op when TV
* input service knows that the next main session corresponds to another HDMI logical
* device. Practically, this implies that one TV input service should handle all HDMI port
* and HDMI-CEC logical devices for smooth active source transition.
- * </p>
*
* @param isMain If true, session should become main.
* @see TvView#setMain
@@ -699,15 +701,15 @@
/**
* Sets the {@link Surface} for the current input session on which the TV input renders
* video.
- * <p>
- * When {@code setSurface(null)} is called, the implementation should stop using the Surface
- * object previously given and release any references to it.
+ *
+ * <p>When {@code setSurface(null)} is called, the implementation should stop using the
+ * Surface object previously given and release any references to it.
*
* @param surface possibly {@code null} {@link Surface} the application passes to this TV
* input session.
* @return {@code true} if the surface was set, {@code false} otherwise.
*/
- public abstract boolean onSetSurface(Surface surface);
+ public abstract boolean onSetSurface(@Nullable Surface surface);
/**
* Called after any structural changes (format or size) have been made to the
@@ -772,8 +774,8 @@
/**
* Enables or disables the caption.
- * <p>
- * The locale for the user's preferred captioning language can be obtained by calling
+ *
+ * <p>The locale for the user's preferred captioning language can be obtained by calling
* {@link CaptioningManager#getLocale CaptioningManager.getLocale()}.
*
* @param enabled {@code true} to enable, {@code false} to disable.
@@ -783,14 +785,13 @@
/**
* Requests to unblock the content according to the given rating.
- * <p>
- * The implementation should unblock the content.
+ *
+ * <p>The implementation should unblock the content.
* TV input service has responsibility to decide when/how the unblock expires
* while it can keep previously unblocked ratings in order not to ask a user
* to unblock whenever a content rating is changed.
* Therefore an unblocked rating can be valid for a channel, a program,
* or certain amount of time depending on the implementation.
- * </p>
*
* @param unblockedRating An unblocked content rating
*/
@@ -799,10 +800,10 @@
/**
* Selects a given track.
- * <p>
- * If this is done successfully, the implementation should call {@link #notifyTrackSelected}
- * to help applications maintain the up-to-date list of the selected tracks.
- * </p>
+ *
+ * <p>If this is done successfully, the implementation should call
+ * {@link #notifyTrackSelected} to help applications maintain the up-to-date list of the
+ * selected tracks.
*
* @param trackId The ID of the track to select. {@code null} means to unselect the current
* track for a given type.
@@ -812,7 +813,7 @@
* @return {@code true} if the track selection was successful, {@code false} otherwise.
* @see #notifyTrackSelected
*/
- public boolean onSelectTrack(int type, String trackId) {
+ public boolean onSelectTrack(int type, @Nullable String trackId) {
return false;
}
@@ -828,7 +829,7 @@
* @hide
*/
@SystemApi
- public void onAppPrivateCommand(String action, Bundle data) {
+ public void onAppPrivateCommand(@NonNull String action, Bundle data) {
}
/**
@@ -883,10 +884,9 @@
/**
* Called when the application sets playback rate and audio mode.
- * <p>
- * Once a playback rate is set, the implementation should honor the value until a new tune
- * request. Pause/resume/seek request does not reset the playback rate previously set.
- * </p>
+ *
+ * <p>Once a playback rate is set, the implementation should honor the value until a new
+ * tune request. Pause/resume/seek request does not reset the playback rate previously set.
*
* @param rate The ratio between desired playback rate and normal one.
* @param audioMode Audio playback mode. Must be one of the supported audio modes:
@@ -906,13 +906,12 @@
* Returns the start playback position for time shifting, in milliseconds since the epoch.
* Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
* moment.
- * <p>
- * The start playback position of the time shifted program should be adjusted when the
+ *
+ * <p>The start playback position of the time shifted program should be adjusted when the
* implementation cannot retain the whole recorded program due to some reason (e.g.
* limitation on storage space). It is the earliest possible time position that the user can
* seek to, thus failure to notifying its change immediately might result in bad experience
* where the application allows the user to seek to an invalid time position.
- * </p>
*
* @see #onTimeShiftResume
* @see #onTimeShiftPause
@@ -942,11 +941,11 @@
/**
* Default implementation of {@link android.view.KeyEvent.Callback#onKeyDown(int, KeyEvent)
* KeyEvent.Callback.onKeyDown()}: always returns false (doesn't handle the event).
- * <p>
- * Override this to intercept key down events before they are processed by the application.
- * If you return true, the application will not process the event itself. If you return
- * false, the normal application processing will occur as if the TV input had not seen the
- * event at all.
+ *
+ * <p>Override this to intercept key down events before they are processed by the
+ * application. If you return true, the application will not process the event itself. If
+ * you return false, the normal application processing will occur as if the TV input had not
+ * seen the event at all.
*
* @param keyCode The value in event.getKeyCode().
* @param event Description of the key event.
@@ -962,8 +961,8 @@
* Default implementation of
* {@link android.view.KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle the event).
- * <p>
- * Override this to intercept key long press events before they are processed by the
+ *
+ * <p>Override this to intercept key long press events before they are processed by the
* application. If you return true, the application will not process the event itself. If
* you return false, the normal application processing will occur as if the TV input had not
* seen the event at all.
@@ -982,11 +981,11 @@
* Default implementation of
* {@link android.view.KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
* KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle the event).
- * <p>
- * Override this to intercept special key multiple events before they are processed by the
- * application. If you return true, the application will not itself process the event. If
- * you return false, the normal application processing will occur as if the TV input had not
- * seen the event at all.
+ *
+ * <p>Override this to intercept special key multiple events before they are processed by
+ * the application. If you return true, the application will not itself process the event.
+ * If you return false, the normal application processing will occur as if the TV input had
+ * not seen the event at all.
*
* @param keyCode The value in event.getKeyCode().
* @param count The number of times the action was made.
@@ -1002,9 +1001,9 @@
/**
* Default implementation of {@link android.view.KeyEvent.Callback#onKeyUp(int, KeyEvent)
* KeyEvent.Callback.onKeyUp()}: always returns false (doesn't handle the event).
- * <p>
- * Override this to intercept key up events before they are processed by the application. If
- * you return true, the application will not itself process the event. If you return false,
+ *
+ * <p>Override this to intercept key up events before they are processed by the application.
+ * If you return true, the application will not itself process the event. If you return false,
* the normal application processing will occur as if the TV input had not seen the event at
* all.
*
@@ -1427,8 +1426,8 @@
/**
* Base class for a TV input session which represents an external device connected to a
* hardware TV input.
- * <p>
- * This class is for an input which provides channels for the external set-top box to the
+ *
+ * <p>This class is for an input which provides channels for the external set-top box to the
* application. Once a TV input returns an implementation of this class on
* {@link #onCreateSession(String)}, the framework will create a separate session for
* a hardware TV Input (e.g. HDMI 1) and forward the application's surface to the session so
@@ -1436,9 +1435,10 @@
* this TV input. The implementation of this class is expected to change the channel of the
* external set-top box via a proprietary protocol when {@link HardwareSession#onTune(Uri)} is
* requested by the application.
- * </p><p>
- * Note that this class is not for inputs for internal hardware like built-in tuner and HDMI 1.
- * </p>
+ *
+ * <p>Note that this class is not for inputs for internal hardware like built-in tuner and HDMI
+ * 1.
+ *
* @see #onCreateSession(String)
*/
public abstract static class HardwareSession extends Session {
@@ -1459,12 +1459,11 @@
/**
* Returns the hardware TV input ID the external device is connected to.
- * <p>
- * TV input is expected to provide {@link android.R.attr#setupActivity} so that
+ *
+ * <p>TV input is expected to provide {@link android.R.attr#setupActivity} so that
* the application can launch it before using this TV input. The setup activity may let
* the user select the hardware TV input to which the external device is connected. The ID
* of the selected one should be stored in the TV input so that it can be returned here.
- * </p>
*/
public abstract String getHardwareInputId();
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 8394517..2c956e9 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -16,10 +16,13 @@
package android.media.tv;
+import android.annotation.NonNull;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
/**
* Encapsulates the format of tracks played in {@link TvInputService}.
*/
@@ -245,15 +248,13 @@
* @param id The ID of the track that uniquely identifies the current track among all the
* other tracks in the same TV program.
*/
- public Builder(int type, String id) {
+ public Builder(int type, @NonNull String id) {
if (type != TYPE_AUDIO
&& type != TYPE_VIDEO
&& type != TYPE_SUBTITLE) {
throw new IllegalArgumentException("Unknown type: " + type);
}
- if (id == null) {
- throw new IllegalArgumentException("id cannot be null");
- }
+ Preconditions.checkNotNull(id);
mType = type;
mId = id;
}
@@ -350,12 +351,11 @@
/**
* Sets the pixel aspect ratio (the ratio of a pixel's width to its height) of the video.
* Valid only for {@link #TYPE_VIDEO} tracks.
- * <p>
- * This is needed for applications to be able to scale the video properly for some video
+ *
+ * <p>This is needed for applications to be able to scale the video properly for some video
* formats such as 720x576 4:3 and 720x576 16:9 where pixels are not square. By default,
* applications assume the value of 1.0 (square pixels), so it is not necessary to set the
* pixel aspect ratio for most video formats.
- * </p>
*
* @param videoPixelAspectRatio The pixel aspect ratio of the video.
*/
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index fd5d6473..d248b12 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,8 @@
package android.media.tv;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.graphics.Canvas;
@@ -52,14 +54,13 @@
* TV programs from various TV sources that implement {@link TvInputService}. (Note that the list of
* TV inputs available on the system can be obtained by calling
* {@link TvInputManager#getTvInputList() TvInputManager.getTvInputList()}.)
- * <p>
- * Once the application supplies the URI for a specific TV channel to {@link #tune(String, Uri)}
+ *
+ * <p>Once the application supplies the URI for a specific TV channel to {@link #tune(String, Uri)}
* method, it takes care of underlying service binding (and unbinding if the current TvView is
* already bound to a service) and automatically allocates/deallocates resources needed. In addition
* to a few essential methods to control how the contents are presented, it also provides a way to
* dispatch input events to the connected TvInputService in order to enable custom key actions for
* the TV input.
- * </p>
*/
public class TvView extends ViewGroup {
private static final String TAG = "TvView";
@@ -174,24 +175,23 @@
* @param callback The callback to receive events. A value of {@code null} removes the existing
* callback.
*/
- public void setCallback(TvInputCallback callback) {
+ public void setCallback(@Nullable TvInputCallback callback) {
mCallback = callback;
}
/**
* Sets this as the main {@link TvView}.
- * <p>
- * The main {@link TvView} is a {@link TvView} whose corresponding TV input determines the
+ *
+ * <p>The main {@link TvView} is a {@link TvView} whose corresponding TV input determines the
* HDMI-CEC active source device. For an HDMI port input, one of source devices that is
* connected to that HDMI port becomes the active source. For an HDMI-CEC logical device input,
* the corresponding HDMI-CEC logical device becomes the active source. For any non-HDMI input
* (including the tuner, composite, S-Video, etc.), the internal device (= TV itself) becomes
* the active source.
- * </p><p>
- * First tuned {@link TvView} becomes main automatically, and keeps to be main until either
+ *
+ * <p>First tuned {@link TvView} becomes main automatically, and keeps to be main until either
* {@link #reset} is called for the main {@link TvView} or {@link #setMain} is called for other
* {@link TvView}.
- * </p>
* @hide
*/
@SystemApi
@@ -274,7 +274,7 @@
* @param inputId The ID of TV input which will play the given channel.
* @param channelUri The URI of a channel.
*/
- public void tune(String inputId, Uri channelUri) {
+ public void tune(@NonNull String inputId, Uri channelUri) {
tune(inputId, channelUri, null);
}
@@ -322,8 +322,8 @@
/**
* Resets this TvView.
- * <p>
- * This method is primarily used to un-tune the current TvView.
+ *
+ * <p>This method is primarily used to un-tune the current TvView.
*/
public void reset() {
if (DEBUG) Log.d(TAG, "reset()");
@@ -344,9 +344,8 @@
/**
* Requests to unblock TV content according to the given rating.
- * <p>
- * This notifies TV input that blocked content is now OK to play.
- * </p>
+ *
+ * <p>This notifies TV input that blocked content is now OK to play.
*
* @param unblockedRating A TvContentRating to unblock.
* @see TvInputService.Session#notifyContentBlocked(TvContentRating)
@@ -361,8 +360,8 @@
/**
* Enables or disables the caption in this TvView.
- * <p>
- * Note that this method does not take any effect unless the current TvView is tuned.
+ *
+ * <p>Note that this method does not take any effect unless the current TvView is tuned.
*
* @param enabled {@code true} to enable, {@code false} to disable.
*/
@@ -473,7 +472,7 @@
* @param callback The callback to receive time shift position changes. A value of {@code null}
* removes the existing callback.
*/
- public void setTimeShiftPositionCallback(TimeShiftPositionCallback callback) {
+ public void setTimeShiftPositionCallback(@Nullable TimeShiftPositionCallback callback) {
mTimeShiftPositionCallback = callback;
ensurePositionTracking();
}
@@ -496,7 +495,7 @@
* @hide
*/
@SystemApi
- public void sendAppPrivateCommand(String action, Bundle data) {
+ public void sendAppPrivateCommand(@NonNull String action, Bundle data) {
if (TextUtils.isEmpty(action)) {
throw new IllegalArgumentException("action cannot be null or an empty string");
}
@@ -511,8 +510,8 @@
/**
* Dispatches an unhandled input event to the next receiver.
- * <p>
- * Except system keys, TvView always consumes input events in the normal flow. This is called
+ *
+ * <p>Except system keys, TvView always consumes input events in the normal flow. This is called
* asynchronously from where the event is dispatched. It gives the host application a chance to
* dispatch the unhandled input events.
*
@@ -797,12 +796,11 @@
/**
* This is called when the start playback position is changed.
- * <p>
- * The start playback position of the time shifted program can be adjusted by the TV input
- * when it cannot retain the whole recorded program due to some reason (e.g. limitation on
- * storage space). The application should not allow the user to seek to a position earlier
- * than the start position.
- * </p>
+ *
+ * <p>The start playback position of the time shifted program can be adjusted by the TV
+ * input when it cannot retain the whole recorded program due to some reason (e.g.
+ * limitation on storage space). The application should not allow the user to seek to a
+ * position earlier than the start position.
*
* @param inputId The ID of the TV input bound to this view.
* @param timeMs The start playback position of the time shifted program, in milliseconds
@@ -963,8 +961,8 @@
public interface OnUnhandledInputEventListener {
/**
* Called when an input event was not handled by the bound TV input.
- * <p>
- * This is called asynchronously from where the event is dispatched. It gives the host
+ *
+ * <p>This is called asynchronously from where the event is dispatched. It gives the host
* application a chance to handle the unhandled input events.
*
* @param event The input event.
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index d2c614e..8c76665 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -338,7 +338,7 @@
status_t res = anw->dequeueBuffer(anw.get(), &anb, &fenceFd);
if (res != OK) {
// TODO: handle different error cases here.
- ALOGE("%s: Set buffer count failed: %s (%d)", __FUNCTION__, strerror(-res), res);
+ ALOGE("%s: Dequeue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
jniThrowRuntimeException(env, "dequeue buffer failed");
return;
}
diff --git a/media/jni/android_media_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp
index 72dacdf..f192262 100644
--- a/media/jni/android_media_MediaSync.cpp
+++ b/media/jni/android_media_MediaSync.cpp
@@ -86,6 +86,10 @@
return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs);
}
+status_t JMediaSync::getPlayTimeForPendingAudioFrames(int64_t *outTimeUs) {
+ return mSync->getPlayTimeForPendingAudioFrames(outTimeUs);
+}
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -268,6 +272,21 @@
return JNI_TRUE;
}
+static jlong android_media_MediaSync_native_getPlayTimeForPendingAudioFrames(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaSync> sync = getMediaSync(env, thiz);
+ if (sync == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ }
+
+ int64_t playTimeUs = 0;
+ status_t err = sync->getPlayTimeForPendingAudioFrames(&playTimeUs);
+ if (err != NO_ERROR) {
+ throwExceptionAsNecessary(env, err);
+ }
+ return (jlong)playTimeUs;
+}
+
static void
android_media_MediaSync_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings)
{
@@ -387,6 +406,10 @@
"(Landroid/media/MediaTimestamp;)Z",
(void *)android_media_MediaSync_native_getTimestamp },
+ { "native_getPlayTimeForPendingAudioFrames",
+ "()J",
+ (void *)android_media_MediaSync_native_getPlayTimeForPendingAudioFrames },
+
{ "native_init", "()V", (void *)android_media_MediaSync_native_init },
{ "native_setup", "()V", (void *)android_media_MediaSync_native_setup },
diff --git a/media/jni/android_media_MediaSync.h b/media/jni/android_media_MediaSync.h
index 9e5de7e..cf81a72 100644
--- a/media/jni/android_media_MediaSync.h
+++ b/media/jni/android_media_MediaSync.h
@@ -41,6 +41,8 @@
status_t setPlaybackRate(float rate);
+ status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs);
+
sp<const MediaClock> getMediaClock();
protected:
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 6f33672..d71b44b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -19,6 +19,7 @@
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceCallbacks;
@@ -170,7 +171,8 @@
assertEquals(CameraBinderTestUtils.NO_ERROR, status);
assertFalse(metadata.isEmpty());
- CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false);
+ CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false,
+ CameraCaptureSession.SESSION_ID_NONE);
assertFalse(request.isEmpty());
assertFalse(metadata.isEmpty());
if (needStream) {
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 2f97809..67d8ab6 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -11,3 +11,5 @@
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index aff57bf..73a723d 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -45,7 +45,6 @@
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import java.io.File;
@@ -55,7 +54,6 @@
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
-import java.util.Objects;
public class ExternalStorageProvider extends DocumentsProvider {
private static final String TAG = "ExternalStorage";
@@ -327,7 +325,7 @@
throw new IllegalArgumentException("Parent document isn't a directory");
}
- final File file = buildUniqueFile(parent, mimeType, displayName);
+ final File file = FileUtils.buildUniqueFile(parent, mimeType, displayName);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
if (!file.mkdir()) {
throw new IllegalStateException("Failed to mkdir " + file);
@@ -345,68 +343,6 @@
return getDocIdForFile(file);
}
- private static File buildFile(File parent, String name, String ext) {
- if (TextUtils.isEmpty(ext)) {
- return new File(parent, name);
- } else {
- return new File(parent, name + "." + ext);
- }
- }
-
- @VisibleForTesting
- public static File buildUniqueFile(File parent, String mimeType, String displayName)
- throws FileNotFoundException {
- String name;
- String ext;
-
- if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- name = displayName;
- ext = null;
- } else {
- String mimeTypeFromExt;
-
- // Extract requested extension from display name
- final int lastDot = displayName.lastIndexOf('.');
- if (lastDot >= 0) {
- name = displayName.substring(0, lastDot);
- ext = displayName.substring(lastDot + 1);
- mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- ext.toLowerCase());
- } else {
- name = displayName;
- ext = null;
- mimeTypeFromExt = null;
- }
-
- if (mimeTypeFromExt == null) {
- mimeTypeFromExt = "application/octet-stream";
- }
-
- final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
- mimeType);
- if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
- // Extension maps back to requested MIME type; allow it
- } else {
- // No match; insist that create file matches requested MIME
- name = displayName;
- ext = extFromMimeType;
- }
- }
-
- File file = buildFile(parent, name, ext);
-
- // If conflicting file, try adding counter suffix
- int n = 0;
- while (file.exists()) {
- if (n++ >= 32) {
- throw new FileNotFoundException("Failed to create unique file");
- }
- file = buildFile(parent, name + " (" + n + ")", ext);
- }
-
- return file;
- }
-
@Override
public String renameDocument(String docId, String displayName) throws FileNotFoundException {
// Since this provider treats renames as generating a completely new
diff --git a/packages/ExternalStorageProvider/tests/Android.mk b/packages/ExternalStorageProvider/tests/Android.mk
deleted file mode 100644
index 830731a..0000000
--- a/packages/ExternalStorageProvider/tests/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_PACKAGE_NAME := ExternalStorageProviderTests
-LOCAL_INSTRUMENTATION_FOR := ExternalStorageProvider
-
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
deleted file mode 100644
index ffcd499..0000000
--- a/packages/ExternalStorageProvider/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.externalstorage.tests">
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.externalstorage"
- android:label="Tests for ExternalStorageProvider" />
-
-</manifest>
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
deleted file mode 100644
index f980b60..0000000
--- a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.externalstorage;
-
-import static com.android.externalstorage.ExternalStorageProvider.buildUniqueFile;
-
-import android.os.FileUtils;
-import android.provider.DocumentsContract.Document;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import java.io.File;
-
-@MediumTest
-public class ExternalStorageProviderTest extends AndroidTestCase {
-
- private File mTarget;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mTarget = getContext().getFilesDir();
- FileUtils.deleteContents(mTarget);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- FileUtils.deleteContents(mTarget);
- }
-
- public void testBuildUniqueFile_normal() throws Exception {
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test"));
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- assertNameEquals("test.jpeg", buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
- assertNameEquals("TEst.JPeg", buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
- assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
- assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png"));
-
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test.flac"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test"));
- assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
- }
-
- public void testBuildUniqueFile_unknown() throws Exception {
- assertNameEquals("test", buildUniqueFile(mTarget, "application/octet-stream", "test"));
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
- assertNameEquals(".test", buildUniqueFile(mTarget, "application/octet-stream", ".test"));
-
- assertNameEquals("test", buildUniqueFile(mTarget, "lolz/lolz", "test"));
- assertNameEquals("test.lolz", buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
- }
-
- public void testBuildUniqueFile_dir() throws Exception {
- assertNameEquals("test", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
- new File(mTarget, "test").mkdir();
- assertNameEquals("test (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
-
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
- new File(mTarget, "test.jpg").mkdir();
- assertNameEquals("test.jpg (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
- }
-
- public void testBuildUniqueFile_increment() throws Exception {
- assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- new File(mTarget, "test.jpg").createNewFile();
- assertNameEquals("test (1).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- new File(mTarget, "test (1).jpg").createNewFile();
- assertNameEquals("test (2).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
- }
-
- private static void assertNameEquals(String expected, File actual) {
- assertEquals(expected, actual.getName());
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
new file mode 100644
index 0000000..1cf7248
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.deviceinfo;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.UserInfo;
+import android.os.Environment;
+import android.os.Handler;
+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;
+import android.os.storage.VolumeInfo;
+import android.util.Log;
+import android.util.SparseLongArray;
+
+import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.ArrayUtils;
+import com.google.android.collect.Sets;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Utility for measuring the disk usage of internal storage or a physical
+ * {@link StorageVolume}. Connects with a remote {@link IMediaContainerService}
+ * and delivers results to {@link MeasurementReceiver}.
+ */
+public class StorageMeasurement {
+ private static final String TAG = "StorageMeasurement";
+
+ private static final boolean LOCAL_LOGV = true;
+ static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
+
+ public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
+
+ /** Media types to measure on external storage. */
+ private static final Set<String> sMeasureMediaTypes = Sets.newHashSet(
+ Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
+ Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_MUSIC,
+ Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
+ Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS,
+ Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
+
+ public static class MeasurementDetails {
+ public long totalSize;
+ public long availSize;
+
+ /**
+ * Total apps disk usage.
+ * <p>
+ * When measuring internal storage, this value includes the code size of
+ * all apps (regardless of install status for current user), and
+ * internal disk used by the current user's apps. When the device
+ * emulates external storage, this value also includes emulated storage
+ * used by the current user's apps.
+ * <p>
+ * When measuring a physical {@link StorageVolume}, this value includes
+ * usage by all apps on that volume.
+ */
+ public long appsSize;
+
+ /**
+ * Total cache disk usage by apps.
+ */
+ public long cacheSize;
+
+ /**
+ * Total media disk usage, categorized by types such as
+ * {@link Environment#DIRECTORY_MUSIC}.
+ * <p>
+ * When measuring internal storage, this reflects media on emulated
+ * storage for the current user.
+ * <p>
+ * When measuring a physical {@link StorageVolume}, this reflects media
+ * on that volume.
+ */
+ public HashMap<String, Long> mediaSize = new HashMap<>();
+
+ /**
+ * Misc external disk usage for the current user, unaccounted in
+ * {@link #mediaSize}.
+ */
+ public long miscSize;
+
+ /**
+ * Total disk usage for users, which is only meaningful for emulated
+ * internal storage. Key is {@link UserHandle}.
+ */
+ public SparseLongArray usersSize = new SparseLongArray();
+ }
+
+ public interface MeasurementReceiver {
+ void onDetailsChanged(MeasurementDetails details);
+ }
+
+ private WeakReference<MeasurementReceiver> mReceiver;
+
+ private final Context mContext;
+
+ private final VolumeInfo mVolume;
+ private final VolumeInfo mSharedVolume;
+
+ private final MainHandler mMainHandler;
+ private final MeasurementHandler mMeasurementHandler;
+
+ public StorageMeasurement(Context context, VolumeInfo volume, VolumeInfo sharedVolume) {
+ mContext = context.getApplicationContext();
+
+ mVolume = volume;
+ mSharedVolume = sharedVolume;
+
+ // Start the thread that will measure the disk usage.
+ final HandlerThread handlerThread = new HandlerThread("MemoryMeasurement");
+ handlerThread.start();
+
+ mMainHandler = new MainHandler();
+ mMeasurementHandler = new MeasurementHandler(handlerThread.getLooper());
+ }
+
+ public void setReceiver(MeasurementReceiver receiver) {
+ if (mReceiver == null || mReceiver.get() == null) {
+ mReceiver = new WeakReference<MeasurementReceiver>(receiver);
+ }
+ }
+
+ public void forceMeasure() {
+ invalidate();
+ measure();
+ }
+
+ public void measure() {
+ if (!mMeasurementHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
+ }
+ }
+
+ public void onDestroy() {
+ mReceiver = null;
+ mMeasurementHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
+ }
+
+ private void invalidate() {
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
+ }
+
+ private static class StatsObserver extends IPackageStatsObserver.Stub {
+ private final boolean mIsPrivate;
+ private final MeasurementDetails mDetails;
+ private final int mCurrentUser;
+ private final Message mFinished;
+
+ private int mRemaining;
+
+ public StatsObserver(boolean isPrivate, MeasurementDetails details, int currentUser,
+ Message finished, int remaining) {
+ mIsPrivate = isPrivate;
+ mDetails = details;
+ mCurrentUser = currentUser;
+ mFinished = finished;
+ mRemaining = remaining;
+ }
+
+ @Override
+ public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+ synchronized (mDetails) {
+ if (succeeded) {
+ addStatsLocked(stats);
+ }
+ if (--mRemaining == 0) {
+ mFinished.sendToTarget();
+ }
+ }
+ }
+
+ private void addStatsLocked(PackageStats stats) {
+ if (mIsPrivate) {
+ long codeSize = stats.codeSize;
+ long dataSize = stats.dataSize;
+ long cacheSize = stats.cacheSize;
+ if (Environment.isExternalStorageEmulated()) {
+ // Include emulated storage when measuring internal. OBB is
+ // shared on emulated storage, so treat as code.
+ codeSize += stats.externalCodeSize + stats.externalObbSize;
+ dataSize += stats.externalDataSize + stats.externalMediaSize;
+ cacheSize += stats.externalCacheSize;
+ }
+
+ // Count code and data for current user
+ if (stats.userHandle == mCurrentUser) {
+ mDetails.appsSize += codeSize;
+ mDetails.appsSize += dataSize;
+ }
+
+ // User summary only includes data (code is only counted once
+ // for the current user)
+ addValue(mDetails.usersSize, stats.userHandle, dataSize);
+
+ // Include cache for all users
+ mDetails.cacheSize += cacheSize;
+
+ } else {
+ // Physical storage; only count external sizes
+ mDetails.appsSize += stats.externalCodeSize + stats.externalDataSize
+ + stats.externalMediaSize + stats.externalObbSize;
+ mDetails.cacheSize += stats.externalCacheSize;
+ }
+ }
+ }
+
+ private class MainHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ final MeasurementDetails details = (MeasurementDetails) msg.obj;
+ final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
+ if (receiver != null) {
+ receiver.onDetailsChanged(details);
+ }
+ }
+ }
+
+ private class MeasurementHandler extends Handler {
+ public static final int MSG_MEASURE = 1;
+ public static final int MSG_CONNECTED = 2;
+ public static final int MSG_DISCONNECT = 3;
+ public static final int MSG_COMPLETED = 4;
+ public static final int MSG_INVALIDATE = 5;
+
+ private Object mLock = new Object();
+
+ private IMediaContainerService mDefaultContainer;
+
+ private volatile boolean mBound = false;
+
+ private MeasurementDetails mCached;
+
+ private final ServiceConnection mDefContainerConn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(
+ service);
+ mDefaultContainer = imcs;
+ mBound = true;
+ sendMessage(obtainMessage(MSG_CONNECTED, imcs));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBound = false;
+ removeMessages(MSG_CONNECTED);
+ }
+ };
+
+ public MeasurementHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_MEASURE: {
+ if (mCached != null) {
+ mMainHandler.obtainMessage(0, mCached).sendToTarget();
+ break;
+ }
+
+ synchronized (mLock) {
+ if (mBound) {
+ removeMessages(MSG_DISCONNECT);
+ sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
+ } else {
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+ mContext.bindServiceAsUser(service, mDefContainerConn,
+ Context.BIND_AUTO_CREATE, UserHandle.OWNER);
+ }
+ }
+ break;
+ }
+ case MSG_CONNECTED: {
+ final IMediaContainerService imcs = (IMediaContainerService) msg.obj;
+ measureExactStorage(imcs);
+ break;
+ }
+ case MSG_DISCONNECT: {
+ synchronized (mLock) {
+ if (mBound) {
+ mBound = false;
+ mContext.unbindService(mDefContainerConn);
+ }
+ }
+ break;
+ }
+ case MSG_COMPLETED: {
+ mCached = (MeasurementDetails) msg.obj;
+ mMainHandler.obtainMessage(0, mCached).sendToTarget();
+ break;
+ }
+ case MSG_INVALIDATE: {
+ mCached = null;
+ break;
+ }
+ }
+ }
+ }
+
+ private void measureExactStorage(IMediaContainerService imcs) {
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ final PackageManager packageManager = mContext.getPackageManager();
+
+ final List<UserInfo> users = userManager.getUsers();
+ final int currentUser = ActivityManager.getCurrentUser();
+
+ final MeasurementDetails details = new MeasurementDetails();
+ final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
+ details);
+
+ if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
+ final File basePath = mSharedVolume.getPathForUser(currentUser);
+
+ // Measure media types for emulated storage, or for primary physical
+ // external volume
+ for (String type : sMeasureMediaTypes) {
+ final File path = new File(basePath, type);
+ final long size = getDirectorySize(imcs, path);
+ details.mediaSize.put(type, size);
+ }
+
+ // Measure misc files not counted under media
+ details.miscSize = measureMisc(imcs, basePath);
+
+ if (mSharedVolume.getType() == VolumeInfo.TYPE_EMULATED) {
+ // Measure total emulated storage of all users; internal apps data
+ // will be spliced in later
+ for (UserInfo user : users) {
+ final File userPath = mSharedVolume.getPathForUser(user.id);
+ final long size = getDirectorySize(imcs, userPath);
+ addValue(details.usersSize, user.id, size);
+ }
+ }
+ }
+
+ 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(
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
+
+ final List<ApplicationInfo> volumeApps = new ArrayList<>();
+ for (ApplicationInfo app : apps) {
+ if (Objects.equals(app.volumeUuid, mVolume.getFsUuid())) {
+ volumeApps.add(app);
+ }
+ }
+
+ final int count = users.size() * volumeApps.size();
+ if (count == 0) {
+ finished.sendToTarget();
+ return;
+ }
+
+ final StatsObserver observer = new StatsObserver(
+ true, details, currentUser, finished, count);
+ for (UserInfo user : users) {
+ for (ApplicationInfo app : volumeApps) {
+ packageManager.getPackageSizeInfo(app.packageName, user.id, observer);
+ }
+ }
+
+ } else {
+ finished.sendToTarget();
+ return;
+ }
+ }
+
+ private static long getDirectorySize(IMediaContainerService imcs, File path) {
+ try {
+ final long size = imcs.calculateDirectorySize(path.toString());
+ Log.d(TAG, "getDirectorySize(" + path + ") returned " + size);
+ return size;
+ } catch (Exception e) {
+ Log.w(TAG, "Could not read memory from default container service for " + path, e);
+ return 0;
+ }
+ }
+
+ private long measureMisc(IMediaContainerService imcs, File dir) {
+ final File[] files = dir.listFiles();
+ if (ArrayUtils.isEmpty(files)) return 0;
+
+ // Get sizes of all top level nodes except the ones already computed
+ long miscSize = 0;
+ for (File file : files) {
+ final String name = file.getName();
+ if (sMeasureMediaTypes.contains(name)) {
+ continue;
+ }
+
+ if (file.isFile()) {
+ miscSize += file.length();
+ } else if (file.isDirectory()) {
+ miscSize += getDirectorySize(imcs, file);
+ }
+ }
+ return miscSize;
+ }
+
+ private static void addValue(SparseLongArray array, int key, long value) {
+ array.put(key, array.get(key) + value);
+ }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 35e9636..5b4b4fd 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -94,6 +94,7 @@
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
+ <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 292c9c2..0d331d1 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -328,7 +328,7 @@
int fillColor = getFillColor(darkIntensity);
mIconTint = fillColor;
mFramePaint.setColor(backgroundColor);
- mBoltPaint.setColor(backgroundColor);
+ mBoltPaint.setColor(fillColor);
mChargeColor = fillColor;
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index b6dbfce..c854d63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3249,6 +3249,10 @@
mKeyguardFadingAway = false;
}
+ public void stopWaitingForKeyguardExit() {
+ mWaitingForKeyguardExit = false;
+ }
+
private void updatePublicMode() {
setLockscreenPublicMode(
mStatusBarKeyguardViewManager.isShowing() && mStatusBarKeyguardViewManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 194a19a..0caf51a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -145,6 +145,7 @@
if (mShowing) {
if (mOccluded) {
mPhoneStatusBar.hideKeyguard();
+ mPhoneStatusBar.stopWaitingForKeyguardExit();
mBouncer.hide(false /* destroyView */);
} else {
showBouncerOrKeyguard();
diff --git a/preloaded-classes b/preloaded-classes
index 86bd5c9..c94623a 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -402,6 +402,7 @@
android.app.SharedPreferencesImpl$EditorImpl$1
android.app.SharedPreferencesImpl$EditorImpl$2
android.app.SharedPreferencesImpl$MemoryCommitResult
+android.app.SystemServiceRegistry
android.app.admin.DevicePolicyManager
android.app.admin.IDevicePolicyManager$Stub
android.app.admin.IDevicePolicyManager$Stub$Proxy
@@ -828,6 +829,11 @@
android.hardware.usb.UsbDevice
android.hardware.usb.UsbDeviceConnection
android.hardware.usb.UsbRequest
+# Initializing android.icu.impl.ICUBinary loads the ICU data.
+# Opening the files in the Zygote avoids StrictMode violations.
+# It also ensures the ICU data files are mapped on boot and all
+# apps will be consistent (even if files are added to /data).
+android.icu.impl.ICUBinary
android.inputmethodservice.ExtractEditText
android.location.Location
android.location.Location$1
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a31a1a7..5df74c5 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -220,6 +220,35 @@
setString("migrated_biometric_weak", "true", 0);
Slog.i(TAG, "Migrated biometric weak to use the fallback instead");
}
+
+ // Migrates lockscreen.disabled. Prior to M, the flag was ignored when more than one
+ // user was present on the system, so if we're upgrading to M and there is more than one
+ // user we disable the flag to remain consistent.
+ if (getString("migrated_lockscreen_disabled", null, 0) == null) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+
+ final List<UserInfo> users = um.getUsers();
+ final int userCount = users.size();
+ int switchableUsers = 0;
+ for (int i = 0; i < userCount; i++) {
+ if (users.get(i).supportsSwitchTo()) {
+ switchableUsers++;
+ }
+ }
+
+ if (switchableUsers > 1) {
+ for (int i = 0; i < userCount; i++) {
+ int id = users.get(i).id;
+
+ if (getBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id)) {
+ setBoolean(LockPatternUtils.DISABLE_LOCKSCREEN_KEY, false, id);
+ }
+ }
+ }
+
+ setString("migrated_lockscreen_disabled", "true", 0);
+ Slog.i(TAG, "Migrated lockscreen disabled flag");
+ }
} catch (RemoteException re) {
Slog.e(TAG, "Unable to migrate old data", re);
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index f88802a..81088c4 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -232,7 +232,12 @@
public static final int FstrimCompleted = 700;
}
+ private static final int VERSION_INIT = 1;
+ private static final int VERSION_ADD_PRIMARY = 2;
+
private static final String TAG_VOLUMES = "volumes";
+ private static final String ATTR_VERSION = "version";
+ private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
private static final String TAG_VOLUME = "volume";
private static final String ATTR_TYPE = "type";
private static final String ATTR_FS_UUID = "fsUuid";
@@ -302,6 +307,8 @@
/** Map from UUID to metadata */
@GuardedBy("mLock")
private ArrayMap<String, VolumeMetadata> mMetadata = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private String mPrimaryStorageUuid;
/** Map from disk ID to latches */
@GuardedBy("mLock")
@@ -943,22 +950,25 @@
}
private void onDiskScannedLocked(DiskInfo disk) {
+ final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.WRITE_MEDIA_STORAGE);
+
final CountDownLatch latch = mDiskScanLatches.remove(disk.id);
if (latch != null) {
latch.countDown();
}
- boolean empty = true;
+ int volumeCount = 0;
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
if (Objects.equals(disk.id, vol.getDiskId())) {
- empty = false;
+ volumeCount++;
}
}
- if (empty) {
- mCallbacks.notifyDiskUnsupported(disk);
- }
+ mCallbacks.notifyDiskScanned(disk, volumeCount);
}
private void onVolumeCreatedLocked(VolumeInfo vol) {
@@ -1022,8 +1032,8 @@
if (isBroadcastWorthy(vol)) {
final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- // TODO: require receiver to hold permission
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.WRITE_MEDIA_STORAGE);
}
final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
@@ -1166,7 +1176,21 @@
while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) {
final String tag = in.getName();
- if (TAG_VOLUME.equals(tag)) {
+ if (TAG_VOLUMES.equals(tag)) {
+ final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
+ if (version >= VERSION_ADD_PRIMARY) {
+ mPrimaryStorageUuid = readStringAttribute(in,
+ ATTR_PRIMARY_STORAGE_UUID);
+ } else {
+ if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL,
+ false)) {
+ mPrimaryStorageUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
+ } else {
+ mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL;
+ }
+ }
+
+ } else if (TAG_VOLUME.equals(tag)) {
final VolumeMetadata meta = VolumeMetadata.read(in);
mMetadata.put(meta.fsUuid, meta);
}
@@ -1192,6 +1216,8 @@
out.setOutput(fos, "utf-8");
out.startDocument(null, true);
out.startTag(null, TAG_VOLUMES);
+ writeIntAttribute(out, ATTR_VERSION, VERSION_ADD_PRIMARY);
+ writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
final int size = mMetadata.size();
for (int i = 0; i < size; i++) {
final VolumeMetadata meta = mMetadata.valueAt(i);
@@ -1398,6 +1424,24 @@
}
@Override
+ public String getPrimaryStorageUuid() throws RemoteException {
+ synchronized (mLock) {
+ return mPrimaryStorageUuid;
+ }
+ }
+
+ @Override
+ public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException {
+ synchronized (mLock) {
+ Slog.d(TAG, "Changing primary storage UUID to " + volumeUuid);
+ mPrimaryStorageUuid = volumeUuid;
+ writeMetadataLocked();
+
+ // TODO: reevaluate all volumes we know about!
+ }
+ }
+
+ @Override
public int[] getStorageUsers(String path) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
@@ -1714,6 +1758,9 @@
@Override
public void finishMediaUpdate() {
+ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException("no permission to call finishMediaUpdate()");
+ }
if (mUnmountSignal != null) {
mUnmountSignal.countDown();
} else {
@@ -1747,7 +1794,7 @@
warnOnNotMounted();
final ObbState state;
- synchronized (mObbPathToStateMap) {
+ synchronized (mObbMounts) {
state = mObbPathToStateMap.get(rawPath);
}
if (state == null) {
@@ -1799,7 +1846,7 @@
Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
final ObbState existingState;
- synchronized (mObbPathToStateMap) {
+ synchronized (mObbMounts) {
existingState = mObbPathToStateMap.get(rawPath);
}
@@ -2049,6 +2096,8 @@
@Override
public String getPassword() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
+ "only keyguard can retrieve password");
if (!isReady()) {
return new String();
}
@@ -2700,7 +2749,7 @@
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
private static final int MSG_VOLUME_METADATA_CHANGED = 3;
- private static final int MSG_DISK_UNSUPPORTED = 4;
+ private static final int MSG_DISK_SCANNED = 4;
private final RemoteCallbackList<IMountServiceListener>
mCallbacks = new RemoteCallbackList<>();
@@ -2748,8 +2797,8 @@
callback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
break;
}
- case MSG_DISK_UNSUPPORTED: {
- callback.onDiskUnsupported((DiskInfo) args.arg1);
+ case MSG_DISK_SCANNED: {
+ callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
break;
}
}
@@ -2777,10 +2826,11 @@
obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
}
- private void notifyDiskUnsupported(DiskInfo disk) {
+ private void notifyDiskScanned(DiskInfo disk, int volumeCount) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = disk;
- obtainMessage(MSG_DISK_UNSUPPORTED, args).sendToTarget();
+ args.argi2 = volumeCount;
+ obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1b32f57..999e91b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -734,12 +734,15 @@
throw new IllegalArgumentException("account is null");
}
checkAuthenticateAccountsPermission(account);
-
- final UserAccounts accounts = getUserAccountsForCaller();
int userId = Binder.getCallingUserHandle().getIdentifier();
if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
return false;
}
+ return updateLastAuthenticatedTime(account);
+ }
+
+ private boolean updateLastAuthenticatedTime(Account account) {
+ final UserAccounts accounts = getUserAccountsForCaller();
synchronized (accounts.cacheLock) {
final ContentValues values = new ContentValues();
values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
@@ -2022,7 +2025,7 @@
try {
new Session(accounts, response, account.type, expectActivityLaunch,
true /* stripAuthTokenFromResult */, account.name,
- true /* authDetailsRequired */) {
+ true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
@Override
public void run() throws RemoteException {
mAuthenticator.confirmCredentials(this, account, options);
@@ -2059,7 +2062,7 @@
try {
new Session(accounts, response, account.type, expectActivityLaunch,
true /* stripAuthTokenFromResult */, account.name,
- false /* authDetailsRequired */) {
+ false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
@Override
public void run() throws RemoteException {
mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
@@ -2492,6 +2495,11 @@
final String mAccountName;
// Indicates if we need to add auth details(like last credential time)
final boolean mAuthDetailsRequired;
+ // If set, we need to update the last authenticated time. This is
+ // currently
+ // used on
+ // successful confirming credentials.
+ final boolean mUpdateLastAuthenticatedTime;
public int mNumResults = 0;
private int mNumRequestContinued = 0;
@@ -2505,6 +2513,13 @@
public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
boolean authDetailsRequired) {
+ this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
+ accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
+ }
+
+ public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
+ boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
+ boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
super();
//if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null");
@@ -2516,6 +2531,7 @@
mCreationTime = SystemClock.elapsedRealtime();
mAccountName = accountName;
mAuthDetailsRequired = authDetailsRequired;
+ mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
synchronized (mSessions) {
mSessions.put(toString(), this);
@@ -2651,15 +2667,55 @@
public void onResult(Bundle result) {
mNumResults++;
Intent intent = null;
- if (result != null && mAuthDetailsRequired) {
- long lastAuthenticatedTime = DatabaseUtils.longForQuery(
- mAccounts.openHelper.getReadableDatabase(),
- "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from " +
- TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
- + ACCOUNTS_TYPE + "=?",
- new String[]{mAccountName, mAccountType});
- result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
- lastAuthenticatedTime);
+ if (result != null) {
+ boolean isSuccessfulConfirmCreds = result.getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT, false);
+ boolean isSuccessfulUpdateCreds =
+ result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
+ && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
+ // We should only update lastAuthenticated time, if
+ // mUpdateLastAuthenticatedTime is true and the confirmRequest
+ // or updateRequest was successful
+ boolean needUpdate = mUpdateLastAuthenticatedTime
+ && (isSuccessfulConfirmCreds || isSuccessfulUpdateCreds);
+ if (needUpdate || mAuthDetailsRequired) {
+ boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
+ if (needUpdate && accountPresent) {
+ updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
+ }
+ if (mAuthDetailsRequired) {
+ long lastAuthenticatedTime = -1;
+ if (accountPresent) {
+ lastAuthenticatedTime = DatabaseUtils.longForQuery(
+ mAccounts.openHelper.getReadableDatabase(),
+ "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+ + " from " +
+ TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+ + ACCOUNTS_TYPE + "=?",
+ new String[] {
+ mAccountName, mAccountType
+ });
+ }
+ result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+ lastAuthenticatedTime);
+ }
+ }
+ if (mAuthDetailsRequired) {
+ long lastAuthenticatedTime = -1;
+ if (isAccountPresentForCaller(mAccountName, mAccountType)) {
+ lastAuthenticatedTime = DatabaseUtils.longForQuery(
+ mAccounts.openHelper.getReadableDatabase(),
+ "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from "
+ +
+ TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+ + ACCOUNTS_TYPE + "=?",
+ new String[] {
+ mAccountName, mAccountType
+ });
+ }
+ result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+ lastAuthenticatedTime);
+ }
}
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
@@ -3202,6 +3258,17 @@
return false;
}
+ private boolean isAccountPresentForCaller(String accountName, String accountType) {
+ if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
+ for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
+ if (account.name.equals(accountName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
int callerUid) {
if (callerUid == Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a48a4d9..4970e0f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5508,17 +5508,20 @@
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
- app.kill(reason, true);
- handleAppDiedLocked(app, true, allowRestart);
- removeLruProcessLocked(app);
-
+ boolean willRestart = false;
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
- addAppLocked(app.info, false, null /* ABI override */);
+ willRestart = true;
} else {
needRestart = true;
}
}
+ app.kill(reason, true);
+ handleAppDiedLocked(app, willRestart, allowRestart);
+ if (willRestart) {
+ removeLruProcessLocked(app);
+ addAppLocked(app.info, false, null /* ABI override */);
+ }
} else {
mRemovedProcesses.add(app);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9a30f0d..6b56279 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -86,6 +86,8 @@
import android.provider.Settings.System;
import android.telecom.TelecomManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
@@ -110,10 +112,8 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
-import java.util.Set;
/**
* The implementation of the volume manager service.
@@ -407,8 +407,7 @@
return "0x" + Integer.toHexString(device) + ":" + deviceAddress;
}
- private final HashMap<String, DeviceListSpec> mConnectedDevices =
- new HashMap<String, DeviceListSpec>();
+ private final ArrayMap<String, DeviceListSpec> mConnectedDevices = new ArrayMap<>();
// Forced device usage for communications
private int mForcedUseForComm;
@@ -2830,16 +2829,22 @@
}
}
public void onServiceDisconnected(int profile) {
+ ArraySet<String> toRemove = null;
switch (profile) {
case BluetoothProfile.A2DP:
synchronized (mConnectedDevices) {
synchronized (mA2dpAvrcpLock) {
// Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
- for(Map.Entry<String, DeviceListSpec> entry
- : mConnectedDevices.entrySet()) {
- DeviceListSpec deviceSpec = entry.getValue();
+ for (int i = 0; i < mConnectedDevices.size(); i++) {
+ DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
if (deviceSpec.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
- makeA2dpDeviceUnavailableNow(deviceSpec.mDeviceAddress);
+ toRemove = toRemove != null ? toRemove : new ArraySet<String>();
+ toRemove.add(deviceSpec.mDeviceAddress);
+ }
+ }
+ if (toRemove != null) {
+ for (int i = 0; i < toRemove.size(); i++) {
+ makeA2dpDeviceUnavailableNow(toRemove.valueAt(i));
}
}
}
@@ -2849,11 +2854,16 @@
case BluetoothProfile.A2DP_SINK:
synchronized (mConnectedDevices) {
// Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices
- for(Map.Entry<String, DeviceListSpec> entry
- : mConnectedDevices.entrySet()) {
- DeviceListSpec deviceSpec = entry.getValue();
+ for(int i = 0; i < mConnectedDevices.size(); i++) {
+ DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
if (deviceSpec.mDeviceType == AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) {
- makeA2dpSrcUnavailable(deviceSpec.mDeviceAddress);
+ toRemove = toRemove != null ? toRemove : new ArraySet<String>();
+ toRemove.add(deviceSpec.mDeviceAddress);
+ }
+ }
+ if (toRemove != null) {
+ for (int i = 0; i < toRemove.size(); i++) {
+ makeA2dpSrcUnavailable(toRemove.valueAt(i));
}
}
}
@@ -4147,11 +4157,8 @@
// Restore device connection states
synchronized (mConnectedDevices) {
- Set set = mConnectedDevices.entrySet();
- Iterator i = set.iterator();
- while (i.hasNext()) {
- Map.Entry device = (Map.Entry)i.next();
- DeviceListSpec spec = (DeviceListSpec)device.getValue();
+ for (int i = 0; i < mConnectedDevices.size(); i++) {
+ DeviceListSpec spec = mConnectedDevices.valueAt(i);
AudioSystem.setDeviceConnectionState(
spec.mDeviceType,
AudioSystem.DEVICE_STATE_AVAILABLE,
@@ -4600,8 +4607,8 @@
int delay = 0;
if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
int devices = 0;
- for (String key : mConnectedDevices.keySet()) {
- int dev = mConnectedDevices.get(key).mDeviceType;
+ for (int i = 0; i < mConnectedDevices.size(); i++) {
+ int dev = mConnectedDevices.valueAt(i).mDeviceType;
if (((dev & AudioSystem.DEVICE_BIT_IN) == 0)
&& ((dev & mBecomingNoisyIntentDevices) != 0)) {
devices |= dev;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index c4f410f..5ac027d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -905,10 +905,14 @@
@ServiceThreadOnly
private void updateArcFeatureStatus(int portId, boolean isConnected) {
assertRunOnServiceThread();
+ HdmiDeviceInfo avr = getAvrDeviceInfo();
+ if (avr == null) {
+ return;
+ }
// HEAC 2.4, HEACT 5-15
// Should not activate ARC if +5V status is false.
HdmiPortInfo portInfo = mService.getPortInfo(portId);
- if (portInfo.isArcSupported()) {
+ if (avr.getPortId() == portId && portInfo.isArcSupported()) {
changeArcFeatureEnabled(portId, isConnected);
}
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index db3ae91..c8e5c3a 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -488,7 +488,7 @@
}
public String encodePublicKey(PublicKey k) throws IOException {
- return new String(Base64.encode(k.getEncoded(), 0));
+ return new String(Base64.encode(k.getEncoded(), Base64.NO_WRAP));
}
public void dumpLPr(PrintWriter pw, String packageName,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 89fa320..a406175 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -717,7 +717,7 @@
} else {
final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
if (vol != null && vol.type == VolumeInfo.TYPE_PRIVATE
- && vol.state == VolumeInfo.STATE_MOUNTED) {
+ && vol.isMountedWritable()) {
return new File(vol.path, "app");
} else {
throw new FileNotFoundException("Failed to find volume for UUID " + volumeUuid);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1c339f5..bd22524 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -49,12 +49,10 @@
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-import static android.content.pm.PackageManager.MOVE_EXTERNAL_MEDIA;
import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
-import static android.content.pm.PackageManager.MOVE_INTERNAL;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
@@ -144,6 +142,7 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
@@ -174,6 +173,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.Xml;
import android.view.Display;
@@ -189,11 +189,14 @@
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.IParcelFileDescriptorFactory;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
+import com.android.server.FgThread;
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -237,6 +240,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -504,6 +508,10 @@
final PackageInstallerService mInstallerService;
private final PackageDexOptimizer mPackageDexOptimizer;
+
+ private AtomicInteger mNextMoveId = new AtomicInteger();
+ private final MoveCallbacks mMoveCallbacks;
+
// Cache of users who need badging.
SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
@@ -1698,6 +1706,7 @@
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(this);
+ mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
getDefaultDisplayMetrics(context, mMetrics);
@@ -14137,49 +14146,25 @@
}
@Override
- public void movePackage(final String packageName, final IPackageMoveObserver observer,
- final int flags) {
+ public int movePackage(final String packageName, final String volumeUuid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
- final int installFlags;
- if ((flags & MOVE_INTERNAL) != 0) {
- installFlags = INSTALL_INTERNAL;
- } else if ((flags & MOVE_EXTERNAL_MEDIA) != 0) {
- installFlags = INSTALL_EXTERNAL;
- } else {
- throw new IllegalArgumentException("Unsupported move flags " + flags);
- }
-
+ final int moveId = mNextMoveId.getAndIncrement();
try {
- movePackageInternal(packageName, null, installFlags, false, observer);
+ movePackageInternal(packageName, volumeUuid, moveId);
} catch (PackageManagerException e) {
Slog.d(TAG, "Failed to move " + packageName, e);
- try {
- observer.packageMoved(packageName, e.error);
- } catch (RemoteException ignored) {
- }
+ mMoveCallbacks.notifyStatusChanged(moveId, PackageManager.MOVE_FAILED_INTERNAL_ERROR);
}
+ return moveId;
}
- @Override
- public void movePackageAndData(final String packageName, final String volumeUuid,
- final IPackageMoveObserver observer) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
- try {
- movePackageInternal(packageName, volumeUuid, INSTALL_INTERNAL, true, observer);
- } catch (PackageManagerException e) {
- Slog.d(TAG, "Failed to move " + packageName, e);
- try {
- observer.packageMoved(packageName, e.error);
- } catch (RemoteException ignored) {
- }
- }
- }
-
- private void movePackageInternal(final String packageName, String volumeUuid, int installFlags,
- boolean andData, final IPackageMoveObserver observer) throws PackageManagerException {
+ private void movePackageInternal(final String packageName, final String volumeUuid,
+ final int moveId) throws PackageManagerException {
final UserHandle user = new UserHandle(UserHandle.getCallingUserId());
+ final PackageManager pm = mContext.getPackageManager();
+ final boolean currentAsec;
final String currentVolumeUuid;
final File codeFile;
final String installerPackageName;
@@ -14205,8 +14190,13 @@
// TODO: yell if already in desired location
+ mMoveCallbacks.notifyStarted(moveId,
+ String.valueOf(pm.getApplicationLabel(pkg.applicationInfo)));
+
pkg.mOperationPending = true;
+ currentAsec = pkg.applicationInfo.isForwardLocked()
+ || pkg.applicationInfo.isExternalAsec();
currentVolumeUuid = ps.volumeUuid;
codeFile = new File(pkg.codePath);
installerPackageName = ps.installerPackageName;
@@ -14215,10 +14205,36 @@
seinfo = pkg.applicationInfo.seinfo;
}
- if (andData) {
- Slog.d(TAG, "Moving " + packageName + " private data from " + currentVolumeUuid + " to "
- + volumeUuid);
+ int installFlags;
+ final boolean moveData;
+
+ if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
+ installFlags = INSTALL_INTERNAL;
+ moveData = !currentAsec;
+ } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
+ installFlags = INSTALL_EXTERNAL;
+ moveData = false;
+ } else {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final VolumeInfo volume = storage.findVolumeByUuid(volumeUuid);
+ if (volume == null || volume.getType() != VolumeInfo.TYPE_PRIVATE
+ || !volume.isMountedWritable()) {
+ throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+ "Move location not mounted private volume");
+ }
+
+ Preconditions.checkState(!currentAsec);
+
+ installFlags = INSTALL_INTERNAL;
+ moveData = true;
+ }
+
+ Slog.d(TAG, "Moving " + packageName + " from " + currentVolumeUuid + " to " + volumeUuid);
+ mMoveCallbacks.notifyStatusChanged(moveId, 10, -1);
+
+ if (moveData) {
synchronized (mInstallLock) {
+ // TODO: split this into separate copy and delete operations
if (mInstaller.moveUserDataDirs(currentVolumeUuid, volumeUuid, packageName, appId,
seinfo) != 0) {
synchronized (mPackages) {
@@ -14234,6 +14250,8 @@
}
}
+ mMoveCallbacks.notifyStatusChanged(moveId, 50);
+
final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) throws RemoteException {
@@ -14259,13 +14277,16 @@
final int status = PackageManager.installStatusToPublicStatus(returnCode);
switch (status) {
case PackageInstaller.STATUS_SUCCESS:
- observer.packageMoved(packageName, PackageManager.MOVE_SUCCEEDED);
+ mMoveCallbacks.notifyStatusChanged(moveId,
+ PackageManager.MOVE_SUCCEEDED);
break;
case PackageInstaller.STATUS_FAILURE_STORAGE:
- observer.packageMoved(packageName, PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE);
+ mMoveCallbacks.notifyStatusChanged(moveId,
+ PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE);
break;
default:
- observer.packageMoved(packageName, PackageManager.MOVE_FAILED_INTERNAL_ERROR);
+ mMoveCallbacks.notifyStatusChanged(moveId,
+ PackageManager.MOVE_FAILED_INTERNAL_ERROR);
break;
}
}
@@ -14283,6 +14304,39 @@
}
@Override
+ public int movePrimaryStorage(String volumeUuid) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
+
+ final int moveId = mNextMoveId.getAndIncrement();
+
+ // TODO: ask mountservice to take down both, connect over to DCS to
+ // migrate, and then bring up new storage
+
+ return moveId;
+ }
+
+ @Override
+ public int getMoveStatus(int moveId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+ return mMoveCallbacks.mLastStatus.get(moveId);
+ }
+
+ @Override
+ public void registerMoveCallback(IPackageMoveObserver callback) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+ mMoveCallbacks.register(callback);
+ }
+
+ @Override
+ public void unregisterMoveCallback(IPackageMoveObserver callback) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null);
+ mMoveCallbacks.unregister(callback);
+ }
+
+ @Override
public boolean setInstallLocation(int loc) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
null);
@@ -14605,4 +14659,82 @@
}
}
}
+
+ private static class MoveCallbacks extends Handler {
+ private static final int MSG_STARTED = 1;
+ private static final int MSG_STATUS_CHANGED = 2;
+
+ private final RemoteCallbackList<IPackageMoveObserver>
+ mCallbacks = new RemoteCallbackList<>();
+
+ private final SparseIntArray mLastStatus = new SparseIntArray();
+
+ public MoveCallbacks(Looper looper) {
+ super(looper);
+ }
+
+ public void register(IPackageMoveObserver callback) {
+ mCallbacks.register(callback);
+ }
+
+ public void unregister(IPackageMoveObserver callback) {
+ mCallbacks.unregister(callback);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final int n = mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ final IPackageMoveObserver callback = mCallbacks.getBroadcastItem(i);
+ try {
+ invokeCallback(callback, msg.what, args);
+ } catch (RemoteException ignored) {
+ }
+ }
+ mCallbacks.finishBroadcast();
+ args.recycle();
+ }
+
+ private void invokeCallback(IPackageMoveObserver callback, int what, SomeArgs args)
+ throws RemoteException {
+ switch (what) {
+ case MSG_STARTED: {
+ callback.onStarted(args.argi1, (String) args.arg2);
+ break;
+ }
+ case MSG_STATUS_CHANGED: {
+ callback.onStatusChanged(args.argi1, args.argi2, (long) args.arg3);
+ break;
+ }
+ }
+ }
+
+ private void notifyStarted(int moveId, String title) {
+ Slog.v(TAG, "Move " + moveId + " started with title " + title);
+
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = moveId;
+ args.arg2 = title;
+ obtainMessage(MSG_STARTED, args).sendToTarget();
+ }
+
+ private void notifyStatusChanged(int moveId, int status) {
+ notifyStatusChanged(moveId, status, -1);
+ }
+
+ private void notifyStatusChanged(int moveId, int status, long estMillis) {
+ Slog.v(TAG, "Move " + moveId + " status " + status);
+
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = moveId;
+ args.argi2 = status;
+ args.arg3 = estMillis;
+ obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget();
+
+ synchronized (mLastStatus) {
+ mLastStatus.put(moveId, status);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e914cd4..a210223 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -281,7 +281,7 @@
Slog.w(WindowManagerService.TAG, "removeAllWindows: removing win=" + win);
}
- service.removeWindowLocked(win.mSession, win);
+ service.removeWindowLocked(win);
}
allAppWindows.clear();
windows.clear();
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index bb53534..c24fcb3 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -78,7 +78,7 @@
WindowState windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
- mService.removeWindowLocked(windowState.mSession, windowState);
+ mService.removeWindowLocked(windowState);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 38fc959..c5bdbb0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2660,11 +2660,11 @@
if (win == null) {
return;
}
- removeWindowLocked(session, win);
+ removeWindowLocked(win);
}
}
- public void removeWindowLocked(Session session, WindowState win) {
+ void removeWindowLocked(WindowState win) {
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cd9689e..ec70879 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1169,10 +1169,10 @@
WindowState win = mService.windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
- mService.removeWindowLocked(mSession, win);
+ mService.removeWindowLocked(win);
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
- mService.removeWindowLocked(mSession, WindowState.this);
+ mService.removeWindowLocked(WindowState.this);
}
}
} catch (IllegalArgumentException ex) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index bbeee8c..b303505 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -83,7 +83,7 @@
WindowState win = windows.get(winNdx);
if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) Slog.w(WindowManagerService.TAG,
"removeAllWindows: removing win=" + win);
- win.mService.removeWindowLocked(win.mSession, win);
+ win.mService.removeWindowLocked(win);
}
windows.clear();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 20c74f1..6fc3103 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1630,20 +1630,25 @@
// sufficiently what is currently set. Note that this is only
// a sanity check in case the two get out of sync; this should
// never normally happen.
- LockPatternUtils utils = new LockPatternUtils(mContext);
- if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
- Slog.w(LOG_TAG, "Active password quality 0x"
- + Integer.toHexString(policy.mActivePasswordQuality)
- + " does not match actual quality 0x"
- + Integer.toHexString(utils.getActivePasswordQuality(userHandle)));
- policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- policy.mActivePasswordLength = 0;
- policy.mActivePasswordUpperCase = 0;
- policy.mActivePasswordLowerCase = 0;
- policy.mActivePasswordLetters = 0;
- policy.mActivePasswordNumeric = 0;
- policy.mActivePasswordSymbols = 0;
- policy.mActivePasswordNonLetter = 0;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ LockPatternUtils utils = new LockPatternUtils(mContext);
+ if (utils.getActivePasswordQuality(userHandle) < policy.mActivePasswordQuality) {
+ Slog.w(LOG_TAG, "Active password quality 0x"
+ + Integer.toHexString(policy.mActivePasswordQuality)
+ + " does not match actual quality 0x"
+ + Integer.toHexString(utils.getActivePasswordQuality(userHandle)));
+ policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ policy.mActivePasswordLength = 0;
+ policy.mActivePasswordUpperCase = 0;
+ policy.mActivePasswordLowerCase = 0;
+ policy.mActivePasswordLetters = 0;
+ policy.mActivePasswordNumeric = 0;
+ policy.mActivePasswordSymbols = 0;
+ policy.mActivePasswordNonLetter = 0;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
validatePasswordOwnerLocked(policy);
@@ -3978,15 +3983,7 @@
+ " for device owner");
}
synchronized (this) {
- if (!allowedToSetDeviceOwnerOnDevice()) {
- throw new IllegalStateException(
- "Trying to set device owner but device is already provisioned.");
- }
-
- if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) {
- throw new IllegalStateException(
- "Trying to set device owner but device owner is already set.");
- }
+ enforceCanSetDeviceOwner();
// Shutting down backup manager service permanently.
long ident = Binder.clearCallingIdentity();
@@ -4004,7 +4001,7 @@
// Device owner is not set and does not exist, set it.
mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName);
} else {
- // Device owner is not set but a profile owner exists, update Device owner state.
+ // Device owner state already exists, update it.
mDeviceOwner.setDeviceOwner(packageName, ownerName);
}
mDeviceOwner.writeOwnerFile();
@@ -4220,43 +4217,23 @@
if (!mHasFeature) {
return false;
}
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
-
- UserInfo info = mUserManager.getUserInfo(userHandle);
- if (info == null) {
- // User doesn't exist.
- throw new IllegalArgumentException(
- "Attempted to set profile owner for invalid userId: " + userHandle);
- }
- if (info.isGuest()) {
- throw new IllegalStateException("Cannot set a profile owner on a guest");
- }
-
if (who == null
|| !DeviceOwner.isInstalledForUser(who.getPackageName(), userHandle)) {
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
synchronized (this) {
- // Only SYSTEM_UID can override the userSetupComplete
- if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID
- && hasUserSetupCompleted(userHandle)) {
- throw new IllegalStateException(
- "Trying to set profile owner but user is already set-up.");
- }
-
+ enforceCanSetProfileOwner(userHandle);
if (mDeviceOwner == null) {
// Device owner state does not exist, create it.
mDeviceOwner = DeviceOwner.createWithProfileOwner(who, ownerName,
userHandle);
- mDeviceOwner.writeOwnerFile();
- return true;
} else {
- // Device owner already exists, update it.
+ // Device owner state already exists, update it.
mDeviceOwner.setProfileOwner(who, ownerName, userHandle);
- mDeviceOwner.writeOwnerFile();
- return true;
}
+ mDeviceOwner.writeOwnerFile();
+ return true;
}
}
@@ -4446,18 +4423,77 @@
}
/**
- * Device owner can only be set on an unprovisioned device. However, if initiated via "adb",
- * we also allow it if no accounts or additional users are present on the device.
+ * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
+ * permission.
+ * The profile owner can only be set before the user setup phase has completed,
+ * except for:
+ * - SYSTEM_UID
+ * - adb if there are not accounts.
*/
- private boolean allowedToSetDeviceOwnerOnDevice() {
- if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
- return true;
+ private void enforceCanSetProfileOwner(int userHandle) {
+ UserInfo info = mUserManager.getUserInfo(userHandle);
+ if (info == null) {
+ // User doesn't exist.
+ throw new IllegalArgumentException(
+ "Attempted to set profile owner for invalid userId: " + userHandle);
}
+ if (info.isGuest()) {
+ throw new IllegalStateException("Cannot set a profile owner on a guest");
+ }
+ if (getProfileOwner(userHandle) != null) {
+ throw new IllegalStateException("Trying to set the profile owner, but profile owner "
+ + "is already set.");
+ }
+ int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+ if (hasUserSetupCompleted(userHandle) &&
+ AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) {
+ throw new IllegalStateException("Not allowed to set the profile owner because "
+ + "there are already some accounts on the profile");
+ }
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ if (hasUserSetupCompleted(userHandle)
+ && UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
+ throw new IllegalStateException("Cannot set the profile owner on a user which is "
+ + "already set-up");
+ }
+ }
- int callingId = Binder.getCallingUid();
- return (callingId == Process.SHELL_UID || callingId == Process.ROOT_UID)
- && mUserManager.getUserCount() == 1
- && AccountManager.get(mContext).getAccounts().length == 0;
+ /**
+ * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
+ * permission.
+ * The device owner can only be set before the setup phase of the primary user has completed,
+ * except for adb if no accounts or additional users are present on the device.
+ */
+ private void enforceCanSetDeviceOwner() {
+ if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) {
+ throw new IllegalStateException("Trying to set the device owner, but device owner "
+ + "is already set.");
+ }
+ int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+ if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+ return;
+ }
+ if (mUserManager.getUserCount() > 1) {
+ throw new IllegalStateException("Not allowed to set the device owner because there "
+ + "are already several users on the device");
+ }
+ if (AccountManager.get(mContext).getAccounts().length > 0) {
+ throw new IllegalStateException("Not allowed to set the device owner because there "
+ + "are already some accounts on the device");
+ }
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+ throw new IllegalStateException("Cannot set the device owner if the device is "
+ + "already set-up");
+ }
}
private void enforceCrossUserPermission(int userHandle) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 3d54dfb..edeeaba 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;
@@ -30,6 +33,7 @@
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 +86,7 @@
static final int MSG_FLUSH_TO_DISK = 1;
static final int MSG_REMOVE_USER = 2;
static final int MSG_INFORM_LISTENERS = 3;
+ static final int MSG_RESET_LAST_TIMESTAMP = 4;
private final Object mLock = new Object();
Handler mHandler;
@@ -279,6 +284,29 @@
}
/**
+ * Forces the app's timestamp to reflect idle or active. If idle, then it rolls back the
+ * last used timestamp to a point in time thats behind the threshold for idle.
+ */
+ void resetLastTimestamp(String packageName, int userId, boolean idle) {
+ synchronized (mLock) {
+ final long timeNow = checkAndGetTimeLocked();
+ final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0);
+
+ final UserUsageStatsService service =
+ getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ final long lastUsed = service.getLastPackageAccessTime(packageName);
+ final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
+ service.setLastTimestamp(packageName, lastTimestamp);
+ // Inform listeners if necessary
+ if (previouslyIdle != idle) {
+ // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ idle ? 1 : 0, packageName));
+ }
+ }
+ }
+
+ /**
* Called by the Binder stub.
*/
void flushToDisk() {
@@ -384,13 +412,41 @@
}
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 +515,10 @@
informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
break;
+ case MSG_RESET_LAST_TIMESTAMP:
+ resetLastTimestamp((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -566,6 +626,46 @@
}
@Override
+ public boolean isAppIdle(String packageName, int userId) {
+ try {
+ userId = ActivityManagerNative.getDefault().handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "isAppIdle", null);
+ } catch (RemoteException re) {
+ return false;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return UsageStatsService.this.isAppIdle(packageName, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setAppIdle(String packageName, boolean idle, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ try {
+ userId = ActivityManagerNative.getDefault().handleIncomingUser(
+ Binder.getCallingPid(), callingUid, userId, false, true,
+ "setAppIdle", null);
+ } catch (RemoteException re) {
+ return;
+ }
+ getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
+ "No permission to change app idle state");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ PackageInfo pi = AppGlobals.getPackageManager()
+ .getPackageInfo(packageName, 0, userId);
+ if (pi == null) return;
+ UsageStatsService.this.setAppIdle(packageName, idle, userId);
+ } catch (RemoteException re) {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 0a9481a..d94759d 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -211,6 +211,17 @@
notifyStatsChanged();
}
+ /**
+ * Sets the last timestamp for each of the intervals.
+ * @param lastTimestamp
+ */
+ void setLastTimestamp(String packageName, long lastTimestamp) {
+ for (IntervalStats stats : mCurrentStats) {
+ stats.update(packageName, lastTimestamp, UsageEvents.Event.NONE);
+ }
+ notifyStatsChanged();
+ }
+
private static final StatCombiner<UsageStats> sUsageStatsCombiner =
new StatCombiner<UsageStats>() {
@Override
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 8f0c6c8..daccf95 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -142,8 +142,10 @@
// add existing alsa devices
File[] files = new File(ALSA_DIRECTORY).listFiles();
- for (int i = 0; i < files.length; i++) {
- alsaFileAdded(files[i].getName());
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ alsaFileAdded(files[i].getName());
+ }
}
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index bbd5e30..2a30384 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -66,9 +66,18 @@
public static final int STATE_DISCONNECTED = 7;
/**
- * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
+ * The state of an outgoing {@code Call} when waiting on user to select a
+ * {@link PhoneAccount} through which to place the call.
*/
- public static final int STATE_PRE_DIAL_WAIT = 8;
+ public static final int STATE_SELECT_PHONE_ACCOUNT = 8;
+
+ /**
+ * @hide
+ * @deprecated use STATE_SELECT_PHONE_ACCOUNT.
+ */
+ @Deprecated
+ @SystemApi
+ public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT;
/**
* The initial state of an outgoing {@code Call}.
@@ -929,7 +938,7 @@
mVideoCall = parcelableCall.getVideoCall();
}
- int state = stateFromParcelableCallState(parcelableCall.getState());
+ int state = parcelableCall.getState();
boolean stateChanged = mState != state;
if (stateChanged) {
mState = state;
@@ -1064,32 +1073,4 @@
callback.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
}
}
-
- private int stateFromParcelableCallState(int parcelableCallState) {
- switch (parcelableCallState) {
- case CallState.NEW:
- return STATE_NEW;
- case CallState.CONNECTING:
- return STATE_CONNECTING;
- case CallState.PRE_DIAL_WAIT:
- return STATE_PRE_DIAL_WAIT;
- case CallState.DIALING:
- return STATE_DIALING;
- case CallState.RINGING:
- return STATE_RINGING;
- case CallState.ACTIVE:
- return STATE_ACTIVE;
- case CallState.ON_HOLD:
- return STATE_HOLDING;
- case CallState.DISCONNECTED:
- return STATE_DISCONNECTED;
- case CallState.ABORTED:
- return STATE_DISCONNECTED;
- case CallState.DISCONNECTING:
- return STATE_DISCONNECTING;
- default:
- Log.wtf(this, "Unrecognized CallState %s", parcelableCallState);
- return STATE_NEW;
- }
- }
}
diff --git a/telecomm/java/android/telecom/CallState.java b/telecomm/java/android/telecom/CallState.java
deleted file mode 100644
index 5584226..0000000
--- a/telecomm/java/android/telecom/CallState.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecom;
-
-/**
- * Defines call-state constants of the different states in which a call can exist. Although states
- * have the notion of normal transitions, due to the volatile nature of telephony systems, code
- * that uses these states should be resilient to unexpected state changes outside of what is
- * considered traditional.
- */
-public final class CallState {
-
- private CallState() {}
-
- /**
- * Indicates that a call is new and not connected. This is used as the default state internally
- * within Telecom and should not be used between Telecom and call services. Call services are
- * not expected to ever interact with NEW calls, but {@link InCallService}s will see calls in
- * this state.
- */
- public static final int NEW = 0;
-
- /**
- * The initial state of an outgoing {@code Call}.
- * Common transitions are to {@link #DIALING} state for a successful call or
- * {@link #DISCONNECTED} if it failed.
- */
- public static final int CONNECTING = 1;
-
- /**
- * Indicates that the call is about to go into the outgoing and dialing state but is waiting for
- * user input before it proceeds. For example, where no default {@link PhoneAccount} is set,
- * this is the state where the InCallUI is waiting for the user to select a
- * {@link PhoneAccount} to call from.
- */
- public static final int PRE_DIAL_WAIT = 2;
-
- /**
- * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
- * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
- * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
- * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user).
- */
- public static final int DIALING = 3;
-
- /**
- * Indicates that a call is incoming and the user still has the option of answering, rejecting,
- * or doing nothing with the call. This state is usually associated with some type of audible
- * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED}
- * otherwise.
- */
- public static final int RINGING = 4;
-
- /**
- * Indicates that a call is currently connected to another party and a communication channel is
- * open between them. The normal transition to this state is by the user answering a
- * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
- */
- public static final int ACTIVE = 5;
-
- /**
- * Indicates that the call is currently on hold. In this state, the call is not terminated
- * but no communication is allowed until the call is no longer on hold. The typical transition
- * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing
- * an action, such as clicking the hold button.
- */
- public static final int ON_HOLD = 6;
-
- /**
- * Indicates that a call is currently disconnected. All states can transition to this state
- * by the call service giving notice that the connection has been severed. When the user
- * explicitly ends a call, it will not transition to this state until the call service confirms
- * the disconnection or communication was lost to the call service currently responsible for
- * this call (e.g., call service crashes).
- */
- public static final int DISCONNECTED = 7;
-
- /**
- * Indicates that the call was attempted (mostly in the context of outgoing, at least at the
- * time of writing) but cancelled before it was successfully connected.
- */
- public static final int ABORTED = 8;
-
- /**
- * Indicates that the call is in the process of being disconnected and will transition next
- * to a {@link #DISCONNECTED} state.
- * <p>
- * This state is not expected to be communicated from the Telephony layer, but will be reported
- * to the InCall UI for calls where disconnection has been initiated by the user but the
- * ConnectionService has confirmed the call as disconnected.
- */
- public static final int DISCONNECTING = 9;
-
- public static String toString(int callState) {
- switch (callState) {
- case NEW:
- return "NEW";
- case CONNECTING:
- return "CONNECTING";
- case PRE_DIAL_WAIT:
- return "PRE_DIAL_WAIT";
- case DIALING:
- return "DIALING";
- case RINGING:
- return "RINGING";
- case ACTIVE:
- return "ACTIVE";
- case ON_HOLD:
- return "ON_HOLD";
- case DISCONNECTED:
- return "DISCONNECTED";
- case ABORTED:
- return "ABORTED";
- case DISCONNECTING:
- return "DISCONNECTING";
- default:
- return "UNKNOWN";
- }
- }
-}
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index eef72fb..bf8fac6 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -87,15 +87,15 @@
}
// No user-set dialer found, fallback to system dialer
- ComponentName systemDialer = getTelecomManager(context).getDefaultPhoneApp();
+ String systemDialer = getTelecomManager(context).getSystemDialerPackage();
- if (systemDialer == null) {
+ if (TextUtils.isEmpty(systemDialer)) {
// No system dialer configured at build time
return null;
}
// Verify that the system dialer has not been disabled.
- return getComponentName(componentNames, systemDialer.getPackageName());
+ return getComponentName(componentNames, systemDialer);
}
/**
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 73bcd0c..3a7faf6 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -130,8 +130,10 @@
/**
* Returns a short label which explains the reason for the disconnect cause and is for display
- * in the user interface. The {@link ConnectionService } is responsible for providing and
- * localizing this label. If there is no string provided, returns null.
+ * in the user interface. If not null, it is expected that the In-Call UI should display this
+ * text where it would normally display the call state ("Dialing", "Disconnected") and is
+ * therefore expected to be relatively small. The {@link ConnectionService } is responsible for
+ * providing and localizing this label. If there is no string provided, returns null.
*
* @return The disconnect label.
*/
@@ -141,8 +143,11 @@
/**
* Returns a description which explains the reason for the disconnect cause and is for display
- * in the user interface. The {@link ConnectionService } is responsible for providing and
- * localizing this message. If there is no string provided, returns null.
+ * in the user interface. This optional text is generally a longer and more descriptive version
+ * of {@link #getLabel}, however it can exist even if {@link #getLabel} is empty. The In-Call UI
+ * should display this relatively prominently; the traditional implementation displays this as
+ * an alert dialog. The {@link ConnectionService} is responsible for providing and localizing
+ * this message. If there is no string provided, returns null.
*
* @return The disconnect description.
*/
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a72172c..8d6bda8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -17,6 +17,7 @@
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -109,6 +110,28 @@
"android.telecom.action.PHONE_ACCOUNT_REGISTERED";
/**
+ * Activity action: Shows a dialog asking the user whether or not they want to replace the
+ * current default Dialer with the one specified in
+ * {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME}.
+ *
+ * Usage example:
+ * <pre>
+ * Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
+ * intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
+ * getActivity().getPackageName());
+ * startActivity(intent);
+ * </pre>
+ */
+ public static final String ACTION_CHANGE_DEFAULT_DIALER =
+ "android.telecom.action.CHANGE_DEFAULT_DIALER";
+
+ /**
+ * Extra value used to provide the package name for {@link #ACTION_CHANGE_DEFAULT_DIALER}.
+ */
+ public static final String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME =
+ "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+
+ /**
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
* determines whether the speakerphone should be automatically turned on for an outgoing call.
*/
@@ -688,7 +711,10 @@
}
}
+
/**
+ * @deprecated - Use {@link TelecomManager#getDefaultDialerPackage} to directly access
+ * the default dialer's package name instead.
* @hide
*/
@SystemApi
@@ -704,6 +730,40 @@
}
/**
+ * Used to determine the currently selected default dialer package.
+ *
+ * @return package name for the default dialer package or null if no package has been
+ * selected as the default dialer.
+ */
+ public String getDefaultDialerPackage() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getDefaultDialerPackage();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
+ }
+ return null;
+ }
+
+ /**
+ * Used to determine the dialer package that is preloaded on the system partition.
+ *
+ * @return package name for the system dialer package or null if no system dialer is preloaded.
+ * @hide
+ */
+ public String getSystemDialerPackage() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().getSystemDialerPackage();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException attempting to get the system dialer package name.", e);
+ }
+ return null;
+ }
+
+ /**
* Return whether a given phone number is the configured voicemail number for a
* particular phone account.
*
@@ -1057,6 +1117,39 @@
}
}
+ /**
+ * Places a new outgoing call to the provided address using the system telecom service with
+ * the specified extras.
+ *
+ * This method is equivalent to placing an outgoing call using {@link Intent#ACTION_CALL},
+ * except that the outgoing call will always be sent via the system telecom service. If
+ * method-caller is either the user selected default dialer app or preloaded system dialer
+ * app, then emergency calls will also be allowed.
+ *
+ * Requires permission: {@link android.Manifest.permission#CALL_PHONE}
+ *
+ * Usage example:
+ * <pre>
+ * Uri uri = Uri.fromParts("tel", "12345", null);
+ * Bundle extras = new Bundle();
+ * extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true);
+ * telecomManager.placeCall(uri, extras);
+ * </pre>
+ *
+ * @param address The address to make the call to.
+ * @param extras Bundle of extras to use with the call.
+ */
+ public void placeCall(Uri address, Bundle extras) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.placeCall(address, extras, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#placeCall", e);
+ }
+ }
+ }
+
private ITelecomService getTelecomService() {
return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 727fd4bb..49f2aad 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -137,6 +137,16 @@
*/
ComponentName getDefaultPhoneApp();
+ /**
+ * @see TelecomServiceImpl#getDefaultDialerPackage
+ */
+ String getDefaultDialerPackage();
+
+ /**
+ * @see TelecomServiceImpl#getSystemDialerPackage
+ */
+ String getSystemDialerPackage();
+
//
// Internal system apis relating to call management.
//
@@ -210,4 +220,9 @@
* @see TelecomServiceImpl#addNewUnknownCall
*/
void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
+
+ /**
+ * @see TelecomServiceImpl#placeCall
+ */
+ void placeCall(in Uri handle, in Bundle extras, String callingPackage);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d103fbf..831a194 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -71,6 +71,11 @@
public static final String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
/**
+ * Control whether users can edit APNs in Settings.
+ */
+ public static final String BOOL_APN_EXPAND = "bool_apn_expand";
+
+ /**
* If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
* this is the value that should be used instead. A configuration value of
* RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -121,6 +126,7 @@
sDefaults.putBoolean(BOOL_CARRIER_VOLTE_PROVISIONED, false);
sDefaults.putBoolean(BOOL_CARRIER_VOLTE_TTY_SUPPORTED, true);
sDefaults.putBoolean(BOOL_SHOW_APN_SETTING_CDMA, false);
+ sDefaults.putBoolean(BOOL_APN_EXPAND, true);
sDefaults.putInt(INT_VOLTE_REPLACEMENT_RAT, 0);
@@ -202,13 +208,13 @@
}
/**
- * Returns a bundle with the default value for every supported configuration variable.
+ * Returns a new bundle with the default value for every supported configuration variable.
*
* @hide
*/
@SystemApi
public static Bundle getDefaultConfig() {
- return sDefaults;
+ return new Bundle(sDefaults);
}
/** @hide */
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 276b713..9efea0d 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -16,7 +16,6 @@
package android.test.mock;
-import android.annotation.NonNull;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Intent;
@@ -29,7 +28,6 @@
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
-import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
@@ -46,11 +44,14 @@
import android.content.pm.ServiceInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.PackageManager.MoveCallback;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Handler;
+import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
@@ -487,36 +488,65 @@
throw new UnsupportedOperationException();
}
- /** {@hide} */
- @Override
- public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
- throw new UnsupportedOperationException();
- }
-
- /** {@hide} */
- @Override
- public void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer) {
- throw new UnsupportedOperationException();
- }
-
- /** {@hide} */
- @Override
- public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
- throw new UnsupportedOperationException();
- }
-
- /** {@hide} */
- @Override
- public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
- throw new UnsupportedOperationException();
- }
-
@Override
public String getInstallerPackageName(String packageName) {
throw new UnsupportedOperationException();
}
+ /** {@hide} */
+ @Override
+ public int getMoveStatus(int moveId) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public void registerMoveCallback(MoveCallback callback, Handler handler) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public void unregisterMoveCallback(MoveCallback callback) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public int movePackage(String packageName, VolumeInfo vol) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public VolumeInfo getPackageCurrentVolume(ApplicationInfo app) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public int movePrimaryStorage(VolumeInfo vol) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public VolumeInfo getPrimaryStorageCurrentVolume() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public List<VolumeInfo> getPrimaryStorageCandidateVolumes() {
+ throw new UnsupportedOperationException();
+ }
+
/**
* @hide - to match hiding in superclass
*/
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>