summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt67
-rw-r--r--api/system-current.txt77
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java45
-rw-r--r--core/java/android/annotation/RequiresPermission.java104
-rw-r--r--core/java/android/app/ActivityManagerNative.java20
-rw-r--r--core/java/android/app/ApplicationPackageManager.java201
-rw-r--r--core/java/android/app/AssistStructure.java11
-rw-r--r--core/java/android/app/IActivityManager.java2
-rw-r--r--core/java/android/app/Notification.java7
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java1
-rw-r--r--core/java/android/content/ContentProvider.java35
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl10
-rw-r--r--core/java/android/content/pm/IPackageMoveObserver.aidl4
-rw-r--r--core/java/android/content/pm/PackageManager.java44
-rw-r--r--core/java/android/os/FileUtils.java75
-rw-r--r--core/java/android/os/storage/DiskInfo.java17
-rw-r--r--core/java/android/os/storage/IMountService.java52
-rw-r--r--core/java/android/os/storage/IMountServiceListener.java16
-rw-r--r--core/java/android/os/storage/StorageEventListener.java2
-rw-r--r--core/java/android/os/storage/StorageManager.java47
-rw-r--r--core/java/android/os/storage/VolumeInfo.java4
-rw-r--r--core/java/android/provider/Settings.java6
-rw-r--r--core/java/android/service/chooser/ChooserTarget.java36
-rw-r--r--core/java/android/view/View.java63
-rw-r--r--core/java/android/view/ViewAssistStructure.java2
-rw-r--r--core/java/android/view/ViewGroup.java32
-rw-r--r--core/java/android/webkit/WebSettings.java8
-rw-r--r--core/java/android/webkit/WebViewFactory.java38
-rw-r--r--core/java/android/widget/Editor.java13
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java5
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java6
-rw-r--r--core/java/com/android/internal/util/Preconditions.java6
-rw-r--r--core/java/com/android/internal/widget/FloatingToolbar.java17
-rw-r--r--core/jni/android_media_AudioRecord.cpp8
-rw-r--r--core/res/res/anim/task_open_enter.xml2
-rw-r--r--core/res/res/anim/wallpaper_close_enter.xml2
-rw-r--r--core/res/res/values/attrs.xml7
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageManagerTests.java82
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java64
-rw-r--r--docs/html/google/play-services/index.jd2
-rw-r--r--docs/html/guide/topics/resources/string-resource.jd30
-rw-r--r--docs/html/reference/com/google/android/gms/fitness/data/Field.html1423
-rw-r--r--docs/html/training/enterprise/app-restrictions.jd14
-rw-r--r--graphics/java/android/graphics/ImageFormat.java143
-rw-r--r--libs/hwui/Android.common.mk1
-rw-r--r--libs/hwui/DeferredDisplayList.h2
-rw-r--r--libs/hwui/tests/Android.mk13
-rw-r--r--libs/hwui/unit_tests/Android.mk34
-rw-r--r--libs/hwui/unit_tests/ClipAreaTests.cpp (renamed from libs/hwui/tests/ClipAreaTests.cpp)0
-rw-r--r--libs/hwui/unit_tests/LinearAllocatorTests.cpp108
-rwxr-xr-xlibs/hwui/unit_tests/how_to_run.txt4
-rw-r--r--libs/hwui/unit_tests/main.cpp22
-rw-r--r--libs/hwui/utils/LinearAllocator.cpp272
-rw-r--r--libs/hwui/utils/LinearAllocator.h142
-rw-r--r--media/java/android/media/AudioRecord.java181
-rw-r--r--media/java/android/media/AudioTrack.java173
-rw-r--r--media/java/android/media/Image.java32
-rw-r--r--media/java/android/media/ImageReader.java2
-rw-r--r--media/java/android/media/MediaCodecInfo.java156
-rw-r--r--media/java/android/media/MediaFormat.java41
-rw-r--r--media/java/android/media/MediaHTTPConnection.java6
-rw-r--r--media/java/android/media/MediaSync.java8
-rw-r--r--media/java/android/media/OnAudioRecordRoutingListener.java29
-rw-r--r--media/java/android/media/OnAudioTrackRoutingListener.java29
-rw-r--r--media/java/android/media/tv/TvContentRating.java22
-rw-r--r--media/java/android/media/tv/TvContract.java567
-rw-r--r--media/java/android/media/tv/TvInputInfo.java9
-rw-r--r--media/java/android/media/tv/TvInputManager.java55
-rw-r--r--media/java/android/media/tv/TvInputService.java140
-rw-r--r--media/java/android/media/tv/TvTrackInfo.java5
-rw-r--r--media/java/android/media/tv/TvView.java51
-rw-r--r--media/jni/android_media_MediaSync.cpp23
-rw-r--r--media/jni/android_media_MediaSync.h2
-rw-r--r--packages/DocumentsUI/Android.mk2
-rw-r--r--packages/DocumentsUI/res/values/strings.xml12
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CopyService.java36
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java15
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java28
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java9
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java66
-rw-r--r--packages/ExternalStorageProvider/tests/Android.mk16
-rw-r--r--packages/ExternalStorageProvider/tests/AndroidManifest.xml13
-rw-r--r--packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java90
-rw-r--r--packages/Keyguard/src/com/android/keyguard/CarrierText.java13
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java441
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java6
-rw-r--r--packages/SystemUI/README.md5
-rw-r--r--packages/SystemUI/docs/demo_mode.md169
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java1
-rw-r--r--preloaded-classes5
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java11
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java29
-rw-r--r--services/core/java/com/android/server/MountService.java71
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java19
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java16
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java242
-rw-r--r--services/core/java/com/android/server/pm/Settings.java7
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java40
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java154
-rw-r--r--telecomm/java/android/telecom/Call.java43
-rw-r--r--telecomm/java/android/telecom/CallState.java134
-rw-r--r--telecomm/java/android/telecom/DefaultDialerManager.java174
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java34
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl5
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java6
-rw-r--r--telephony/java/android/telephony/RadioAccessFamily.java79
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java46
-rw-r--r--tests/VoiceInteraction/res/layout/main.xml6
-rw-r--r--tests/VoiceInteraction/res/xml/interaction_service.xml3
119 files changed, 5012 insertions, 2115 deletions
diff --git a/api/current.txt b/api/current.txt
index 4011ef99d766..fee81f5cc630 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -299,6 +299,7 @@ package android {
field public static final int anyDensity = 16843372; // 0x101026c
field public static final int apduServiceBanner = 16843757; // 0x10103ed
field public static final int apiKey = 16843281; // 0x1010211
+ field public static final int assistBlocked = 16844020; // 0x10104f4
field public static final int author = 16843444; // 0x10102b4
field public static final int authorities = 16842776; // 0x1010018
field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -4071,6 +4072,7 @@ package android.app {
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
+ method public boolean isAssistBlocked();
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
@@ -5009,6 +5011,7 @@ package android.app {
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";
@@ -11334,6 +11337,8 @@ package android.graphics {
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
@@ -11344,6 +11349,8 @@ package android.graphics {
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
}
@@ -14922,16 +14929,20 @@ package android.media {
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);
@@ -14942,8 +14953,10 @@ package android.media {
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;
@@ -14984,12 +14997,14 @@ package android.media {
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();
@@ -15002,6 +15017,7 @@ package android.media {
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();
@@ -15010,6 +15026,7 @@ package android.media {
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);
@@ -15413,22 +15430,24 @@ package android.media {
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
@@ -15447,7 +15466,8 @@ package android.media {
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";
@@ -15815,6 +15835,7 @@ package android.media {
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";
@@ -15824,7 +15845,10 @@ package android.media {
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";
@@ -16423,6 +16447,14 @@ package android.media {
field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -30100,8 +30132,8 @@ package android.telecom {
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 {
@@ -30161,20 +30193,6 @@ package android.telecom {
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();
@@ -30591,6 +30609,7 @@ package android.telecom {
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();
@@ -30655,6 +30674,7 @@ package android.telephony {
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";
@@ -36051,6 +36071,7 @@ package android.view {
method public void invalidateOutline();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
+ method public boolean isAssistBlocked();
method public boolean isAttachedToWindow();
method public boolean isClickable();
method public boolean isDirty();
@@ -36193,6 +36214,7 @@ package android.view {
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setAssistBlocked(boolean);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -36555,6 +36577,7 @@ package android.view {
method public abstract android.view.ViewAssistStructure newChild(int);
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
+ method public abstract void setAssistBlocked(boolean);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index f25521e15eea..137bb19c4a60 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -372,6 +372,7 @@ package android {
field public static final int anyDensity = 16843372; // 0x101026c
field public static final int apduServiceBanner = 16843757; // 0x10103ed
field public static final int apiKey = 16843281; // 0x1010211
+ field public static final int assistBlocked = 16844020; // 0x10104f4
field public static final int author = 16843444; // 0x10102b4
field public static final int authorities = 16842776; // 0x1010018
field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -4162,6 +4163,7 @@ package android.app {
method public int getWidth();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
+ method public boolean isAssistBlocked();
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isClickable();
@@ -5100,6 +5102,7 @@ package android.app {
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";
@@ -11629,6 +11632,8 @@ package android.graphics {
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
@@ -11639,6 +11644,8 @@ package android.graphics {
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
}
@@ -16133,16 +16140,20 @@ package android.media {
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);
@@ -16153,8 +16164,10 @@ package android.media {
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;
@@ -16197,12 +16210,14 @@ package android.media {
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();
@@ -16215,6 +16230,7 @@ package android.media {
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();
@@ -16223,6 +16239,7 @@ package android.media {
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);
@@ -16626,22 +16643,24 @@ package android.media {
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
@@ -16660,7 +16679,8 @@ package android.media {
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";
@@ -17029,6 +17049,7 @@ package android.media {
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";
@@ -17038,7 +17059,10 @@ package android.media {
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";
@@ -17639,6 +17663,14 @@ package android.media {
field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -32206,8 +32238,9 @@ package android.telecom {
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 {
@@ -32271,20 +32304,6 @@ package android.telecom {
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();
@@ -32740,6 +32759,7 @@ package android.telecom {
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();
@@ -32809,6 +32829,7 @@ package android.telephony {
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";
@@ -38251,6 +38272,7 @@ package android.view {
method public void invalidateOutline();
method public boolean isAccessibilityFocused();
method public boolean isActivated();
+ method public boolean isAssistBlocked();
method public boolean isAttachedToWindow();
method public boolean isClickable();
method public boolean isDirty();
@@ -38393,6 +38415,7 @@ package android.view {
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setAssistBlocked(boolean);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -38755,6 +38778,7 @@ package android.view {
method public abstract android.view.ViewAssistStructure newChild(int);
method public abstract void setAccessibilityFocused(boolean);
method public abstract void setActivated(boolean);
+ method public abstract void setAssistBlocked(boolean);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
@@ -41565,10 +41589,19 @@ package android.webkit {
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/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d5cc8cc1557c..b84b1e202888 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.Bundle;
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 @@ public final class Pm {
}
}
- 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 @@ public final class Pm {
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/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java
new file mode 100644
index 000000000000..4aed5c1e9080
--- /dev/null
+++ b/core/java/android/annotation/RequiresPermission.java
@@ -0,0 +1,104 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated element requires (or may require) one or more permissions.
+ * <p/>
+ * Example of requiring a single permission:
+ * <pre>{@code
+ * &#64;RequiresPermission(Manifest.permission.SET_WALLPAPER)
+ * public abstract void setWallpaper(Bitmap bitmap) throws IOException;
+ *
+ * &#64;RequiresPermission(ACCESS_COARSE_LOCATION)
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring at least one permission from a set:
+ * <pre>{@code
+ * &#64;RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring multiple permissions:
+ * <pre>{@code
+ * &#64;RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring separate read and write permissions for a content provider:
+ * <pre>{@code
+ * &#64;RequiresPermission.Read(&#64;RequiresPermission(READ_HISTORY_BOOKMARKS))
+ * &#64;RequiresPermission.Write(&#64;RequiresPermission(WRITE_HISTORY_BOOKMARKS))
+ * public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
+ * }</pre>
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD})
+public @interface RequiresPermission {
+ /**
+ * The name of the permission that is required, if precisely one permission
+ * is required. If more than one permission is required, specify either
+ * {@link #allOf()} or {@link #anyOf()} instead.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #allOf()} must both be null.
+ */
+ String value() default "";
+
+ /**
+ * Specifies a list of permission names that are all required.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #value()} must both be null.
+ */
+ String[] allOf() default {};
+
+ /**
+ * Specifies a list of permission names where at least one is required
+ * <p>
+ * If specified, {@link #allOf()} and {@link #value()} must both be null.
+ */
+ String[] anyOf() default {};
+
+ /**
+ * If true, the permission may not be required in all cases (e.g. it may only be
+ * enforced on certain platforms, or for certain call parameters, etc.
+ */
+ boolean conditional() default false;
+
+ /**
+ * Specifies that the given permission is required for read operations
+ */
+ @Target(FIELD)
+ @interface Read {
+ RequiresPermission value();
+ }
+
+ /**
+ * Specifies that the given permission is required for write operations
+ */
+ @Target(FIELD)
+ @interface Write {
+ RequiresPermission value();
+ }
+}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7f062d9a2bc8..b11c5091854a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2513,6 +2513,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case UPDATE_DEVICE_OWNER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String packageName = data.readString();
+ updateDeviceOwner(packageName);
+ reply.writeNoException();
+ return true;
+ }
+
case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -5801,6 +5809,18 @@ class ActivityManagerProxy implements IActivityManager
}
@Override
+ public void updateDeviceOwner(String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ mRemote.transact(UPDATE_DEVICE_OWNER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
public int getPackageProcessState(String packageName) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index dfe7e18b09a0..10f59602b93f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,6 +62,9 @@ import android.graphics.Rect;
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 com.android.internal.util.UserIcons;
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 @@ final class ApplicationPackageManager extends PackageManager {
@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 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
- public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ public String getInstallerPackageName(String packageName) {
+ try {
+ return mPM.getInstallerPackageName(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return null;
+ }
+
+ @Override
+ public int getMoveStatus(int moveId) {
try {
- mPM.movePackage(packageName, observer, flags);
+ return mPM.getMoveStatus(moveId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer) {
+ 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 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();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int movePackage(String packageName, VolumeInfo vol) {
try {
- mPM.movePackageAndData(packageName, volumeUuid, observer);
+ 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 @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
+ public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
if (app.isInternal()) {
- return Preconditions.checkNotNull(
- storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
+ return 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;
- }
- }
- throw new IllegalStateException("Failed to find primary public volume");
+ return storage.getPrimaryPhysicalVolume();
} else {
- return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid));
+ return storage.findVolumeByUuid(app.volumeUuid);
}
}
@Override
- public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+ 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 @@ final class ApplicationPackageManager extends PackageManager {
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 @@ final class ApplicationPackageManager extends PackageManager {
}
@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 @@ final class ApplicationPackageManager extends PackageManager {
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/AssistStructure.java b/core/java/android/app/AssistStructure.java
index 1e159a3b5add..9946d79ca51f 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -218,6 +218,7 @@ final public class AssistStructure implements Parcelable {
static final int FLAGS_FOCUSED = 0x00000020;
static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x04000000;
static final int FLAGS_SELECTED = 0x00000040;
+ static final int FLAGS_ASSIST_BLOCKED = 0x00000080;
static final int FLAGS_ACTIVATED = 0x40000000;
static final int FLAGS_CHECKABLE = 0x00000100;
static final int FLAGS_CHECKED = 0x00000200;
@@ -356,6 +357,10 @@ final public class AssistStructure implements Parcelable {
return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
}
+ public boolean isAssistBlocked() {
+ return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) == 0;
+ }
+
public boolean isEnabled() {
return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
}
@@ -484,6 +489,12 @@ final public class AssistStructure implements Parcelable {
}
@Override
+ public void setAssistBlocked(boolean state) {
+ mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED)
+ | (state ? 0 : ViewNode.FLAGS_ASSIST_BLOCKED);
+ }
+
+ @Override
public void setEnabled(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
| (state ? 0 : ViewNode.FLAGS_DISABLED);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 7e03faa5a3f6..00558fe15bb2 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -495,6 +495,7 @@ public interface IActivityManager extends IInterface {
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
throws RemoteException;
public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
+ public void updateDeviceOwner(String packageName) throws RemoteException;
public int getPackageProcessState(String packageName) throws RemoteException;
@@ -837,4 +838,5 @@ public interface IActivityManager extends IInterface {
int NOTE_ALARM_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+292;
int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294;
+ int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e7f8f6d6fb3c..2cf23afa23af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5648,6 +5648,13 @@ public class Notification implements Parcelable
/**
* 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/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2418e8242593..79e560ff9f4b 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -792,7 +792,6 @@ public final class BluetoothAdapter {
// 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/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 393cf8e34df1..fd65d56abb1a 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -26,6 +26,7 @@ import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
@@ -204,8 +205,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
- return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
- CancellationSignal.fromTransport(cancellationSignal));
+ // The caller has no access to the data, so return an empty cursor with
+ // the columns in the requested order. The caller may ask for an invalid
+ // column and we would not catch that but this is not a problem in practice.
+ // We do not call ContentProvider#query with a modified where clause since
+ // the implementation is not guaranteed to be backed by a SQL database, hence
+ // it may not handle properly the tautology where clause we would have created.
+ return new MatrixCursor(projection, 0);
}
final String original = setCallingPackage(callingPkg);
try {
@@ -817,31 +823,6 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/**
- * @hide
- * Implementation when a caller has performed a query on the content
- * provider, but that call has been rejected for the operation given
- * to {@link #setAppOps(int, int)}. The default implementation
- * rewrites the <var>selection</var> argument to include a condition
- * that is never true (so will always result in an empty cursor)
- * and calls through to {@link #query(android.net.Uri, String[], String, String[],
- * String, android.os.CancellationSignal)} with that.
- */
- public Cursor rejectQuery(Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- CancellationSignal cancellationSignal) {
- // The read is not allowed... to fake it out, we replace the given
- // selection statement with a dummy one that will always be false.
- // This way we will get a cursor back that has the correct structure
- // but contains no rows.
- if (selection == null || selection.isEmpty()) {
- selection = "'A' = 'B'";
- } else {
- selection = "'A' = 'B' AND (" + selection + ")";
- }
- return query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
- }
-
- /**
* Implement this to handle query requests from clients.
* This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 447c668eefd1..ae59bfcd05a9 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -50,7 +50,6 @@ import android.net.Uri;
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 @@ interface IPackageManager {
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 baa1595aea4c..50ab3b5c6a5f 100644
--- a/core/java/android/content/pm/IPackageMoveObserver.aidl
+++ b/core/java/android/content/pm/IPackageMoveObserver.aidl
@@ -22,6 +22,6 @@ package android.content.pm;
* @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 e4108b1aacc5..e1c271dbe13d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -42,6 +42,7 @@ import android.graphics.drawable.Drawable;
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 @@ public abstract class PackageManager {
*
* @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 @@ public abstract class PackageManager {
* been installed on external media.
* @hide
*/
+ @Deprecated
public static final int MOVE_INTERNAL = 0x00000001;
/**
@@ -948,8 +951,12 @@ public abstract class PackageManager {
* 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 @@ public abstract class PackageManager {
* @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 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 @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app);
+ public abstract int getMoveStatus(int moveId);
/** {@hide} */
- public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app);
+ 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/os/FileUtils.java b/core/java/android/os/FileUtils.java
index b302f95a7828..931cd3e6a1d5 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.IOException;
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 @@ public class FileUtils {
}
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/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 64f2a05b6720..9623695edbba 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -36,7 +36,10 @@ import java.util.Objects;
* @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 class DiskInfo implements Parcelable {
}
}
+ 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 @@ public class DiskInfo implements Parcelable {
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 0a8187e8772c..0b1031cfbe71 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1063,6 +1063,38 @@ public interface IMountService extends IInterface {
_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 @@ public interface IMountService extends IInterface {
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 @@ public interface IMountService extends IInterface {
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 interface IMountService extends IInterface {
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 8e878a4753af..fcb4779de324 100644
--- a/core/java/android/os/storage/IMountServiceListener.java
+++ b/core/java/android/os/storage/IMountServiceListener.java
@@ -98,10 +98,11 @@ public interface IMountServiceListener extends IInterface {
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 @@ public interface IMountServiceListener extends IInterface {
}
@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 @@ public interface IMountServiceListener extends IInterface {
}
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 interface IMountServiceListener extends IInterface {
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 ad2fae0b7600..6a0140e67a00 100644
--- a/core/java/android/os/storage/StorageEventListener.java
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -44,6 +44,6 @@ public class StorageEventListener {
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 f101352294d1..747fb40b92c7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -73,6 +73,11 @@ public class StorageManager {
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 @@ public class StorageManager {
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 @@ public class StorageManager {
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 @@ public class StorageManager {
}
@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 @@ public class StorageManager {
/** {@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 @@ public class StorageManager {
}
/** {@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 f3498d55c779..4e9cfc77e5bd 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -242,6 +242,10 @@ public class VolumeInfo implements Parcelable {
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/Settings.java b/core/java/android/provider/Settings.java
index 8dc27dcf416e..00c851bf7f08 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5391,6 +5391,12 @@ public final class Settings {
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
+ * Specifies the package name currently configured to be the default dialer application
+ * @hide
+ */
+ public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application";
+
+ /**
* Specifies the package name currently configured to be the emergency assistance application
*
* @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java
index d21cc3c81be5..f0ca276f11f2 100644
--- a/core/java/android/service/chooser/ChooserTarget.java
+++ b/core/java/android/service/chooser/ChooserTarget.java
@@ -78,7 +78,8 @@ public final class ChooserTarget implements Parcelable {
* <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 @@ public final class ChooserTarget implements Parcelable {
* <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 @@ public final class ChooserTarget implements Parcelable {
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 @@ public final class ChooserTarget implements Parcelable {
}
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/View.java b/core/java/android/view/View.java
index 9741239d55b0..5c6ce76c8613 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -631,6 +631,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* </p>
*
* @attr ref android.R.styleable#View_alpha
+ * @attr ref android.R.styleable#View_assistBlocked
* @attr ref android.R.styleable#View_background
* @attr ref android.R.styleable#View_clickable
* @attr ref android.R.styleable#View_contentDescription
@@ -2324,6 +2325,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 1 PFLAG3_IS_LAID_OUT
* 1 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT
* 1 PFLAG3_CALLED_SUPER
+ * 1 PFLAG3_APPLYING_INSETS
+ * 1 PFLAG3_FITTING_SYSTEM_WINDOWS
+ * 1 PFLAG3_NESTED_SCROLLING_ENABLED
+ * 1 PFLAG3_ASSIST_BLOCKED
* |-------|-------|-------|-------|
*/
@@ -2381,6 +2386,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
/**
+ * <p>Indicates that we are allowing {@link android.view.ViewAssistStructure} to traverse
+ * into this view.<p>
+ */
+ static final int PFLAG3_ASSIST_BLOCKED = 0x100;
+
+ /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -3869,6 +3880,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
viewFlagMasks |= SAVE_DISABLED_MASK;
}
break;
+ case com.android.internal.R.styleable.View_assistBlocked:
+ if (a.getBoolean(attr, false)) {
+ mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED;
+ }
+ break;
case com.android.internal.R.styleable.View_duplicateParentState:
if (a.getBoolean(attr, false)) {
viewFlagValues |= DUPLICATE_PARENT_STATE;
@@ -5775,7 +5791,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
structure.setId(id, null, null, null);
}
- structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight-mLeft, mBottom-mTop);
+ structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
structure.setVisibility(getVisibility());
structure.setEnabled(isEnabled());
if (isClickable()) {
@@ -5890,8 +5906,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link #onProvideVirtualAssistStructure}.
*/
public void dispatchProvideAssistStructure(ViewAssistStructure structure) {
- onProvideAssistStructure(structure);
- onProvideVirtualAssistStructure(structure);
+ if (!isAssistBlocked()) {
+ onProvideAssistStructure(structure);
+ onProvideVirtualAssistStructure(structure);
+ } else {
+ structure.setClassName(getAccessibilityClassName().toString());
+ structure.setAssistBlocked(true);
+ }
}
/**
@@ -7458,6 +7479,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Indicates whether this view will participate in data collection through
+ * {@link android.view.ViewAssistStructure}. If true, it will not provide any data
+ * for itself or its children. If false, the normal data collection will be allowed.
+ *
+ * @return Returns false if assist data collection is not blocked, else true.
+ *
+ * @see #setAssistBlocked(boolean)
+ * @attr ref android.R.styleable#View_assistBlocked
+ */
+ public boolean isAssistBlocked() {
+ return (mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED) != 0;
+ }
+
+ /**
+ * Controls whether assist data collection from this view and its children is enabled
+ * (that is, whether {@link #onProvideAssistStructure} and
+ * {@link #onProvideVirtualAssistStructure} will be called). The default value is false,
+ * allowing normal assist collection. Setting this to false will disable assist collection.
+ *
+ * @param enabled Set to true to <em>disable</em> assist data collection, or false
+ * (the default) to allow it.
+ *
+ * @see #isAssistBlocked()
+ * @see #onProvideAssistStructure
+ * @see #onProvideVirtualAssistStructure
+ * @attr ref android.R.styleable#View_assistBlocked
+ */
+ public void setAssistBlocked(boolean enabled) {
+ if (enabled) {
+ mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED;
+ } else {
+ mPrivateFlags3 &= ~PFLAG3_ASSIST_BLOCKED;
+ }
+ }
+
+ /**
* Indicates whether this view will save its state (that is,
* whether its {@link #onSaveInstanceState} method will be called).
*
diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java
index 7d263c540764..346b8ec1700c 100644
--- a/core/java/android/view/ViewAssistStructure.java
+++ b/core/java/android/view/ViewAssistStructure.java
@@ -32,6 +32,8 @@ public abstract class ViewAssistStructure {
public abstract void setVisibility(int visibility);
+ public abstract void setAssistBlocked(boolean state);
+
public abstract void setEnabled(boolean state);
public abstract void setClickable(boolean state);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8f2be990e460..4324e752eb4d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2881,21 +2881,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
public void dispatchProvideAssistStructure(ViewAssistStructure structure) {
super.dispatchProvideAssistStructure(structure);
- if (structure.getChildCount() == 0) {
- final int childrenCount = getChildCount();
- if (childrenCount > 0) {
- structure.setChildCount(childrenCount);
- final ArrayList<View> preorderedList = buildOrderedChildList();
- final boolean customOrder = preorderedList == null
- && isChildrenDrawingOrderEnabled();
- final View[] children = mChildren;
- for (int i=0; i<childrenCount; i++) {
- final int childIndex = customOrder
- ? getChildDrawingOrder(childrenCount, i) : i;
- final View child = (preorderedList == null)
- ? children[childIndex] : preorderedList.get(childIndex);
- ViewAssistStructure cstructure = structure.newChild(i);
- child.dispatchProvideAssistStructure(cstructure);
+ if (!isAssistBlocked()) {
+ if (structure.getChildCount() == 0) {
+ final int childrenCount = getChildCount();
+ if (childrenCount > 0) {
+ structure.setChildCount(childrenCount);
+ final ArrayList<View> preorderedList = buildOrderedChildList();
+ final boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ final View[] children = mChildren;
+ for (int i=0; i<childrenCount; i++) {
+ final int childIndex = customOrder
+ ? getChildDrawingOrder(childrenCount, i) : i;
+ final View child = (preorderedList == null)
+ ? children[childIndex] : preorderedList.get(childIndex);
+ ViewAssistStructure cstructure = structure.newChild(i);
+ child.dispatchProvideAssistStructure(cstructure);
+ }
}
}
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 943beb084f99..453e4f5499a0 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -900,7 +900,9 @@ public abstract class WebSettings {
* and therefore secure policy, this setting should be disabled.
* Note that this setting affects only JavaScript access to file scheme
* resources. Other access to such resources, for example, from image HTML
- * elements, is unaffected.
+ * elements, is unaffected. To prevent possible violation of same domain policy
+ * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
+ * devices, you should explicitly set this value to {@code false}.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
@@ -920,7 +922,9 @@ public abstract class WebSettings {
* the value of {@link #getAllowUniversalAccessFromFileURLs} is true.
* Note too, that this setting affects only JavaScript access to file scheme
* resources. Other access to such resources, for example, from image HTML
- * elements, is unaffected.
+ * elements, is unaffected. To prevent possible violation of same domain policy
+ * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
+ * devices, you should explicitly set this value to {@code false}.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 3340c73c32db..9782d7202b73 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -76,6 +76,18 @@ public final class WebViewFactory {
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 @@ public final class WebViewFactory {
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 @@ public final class WebViewFactory {
}
}
- 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 @@ public final class WebViewFactory {
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/Editor.java b/core/java/android/widget/Editor.java
index b049e494f933..652fff265980 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1957,6 +1957,9 @@ public class Editor {
if (mPositionListener != null) {
mPositionListener.onScrollChanged();
}
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.invalidateContentRect();
+ }
}
/**
@@ -3085,10 +3088,12 @@ public class Editor {
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/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8403e77f7368..a6c39e60be03 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -401,6 +401,11 @@ public class ChooserActivity extends ResolverActivity {
}
@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 3cd69a136920..7f51d9267906 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1062,7 +1062,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} 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 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// 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 414b7bc0c407..b692a189a6a7 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -24,6 +24,12 @@ import java.util.Collection;
*/
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/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 579cad42c68b..3a1e0caed2eb 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -433,15 +433,13 @@ public final class FloatingToolbar {
mHidden = false;
mDismissed = false;
- cancelAllAnimations();
+ cancelDismissAndHideAnimations();
+ cancelOverflowAnimations();
// Make sure a panel is set as the content.
if (mContentContainer.getChildCount() == 0) {
setMainPanelAsContent();
}
preparePopupContent();
- // If we're yet to show the popup, set the container visibility to zero.
- // The "show" animation will make this visible.
- mContentContainer.setAlpha(0);
mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y);
setTouchableSurfaceInsetsComputer();
runShowAnimation();
@@ -451,12 +449,13 @@ public final class FloatingToolbar {
* Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
*/
public void dismiss() {
- if (!isShowing()) {
+ if (mDismissed) {
return;
}
mHidden = false;
mDismissed = true;
+ mHideAnimation.cancel();
runDismissAnimation();
setZeroTouchableSurface();
}
@@ -499,7 +498,7 @@ public final class FloatingToolbar {
return;
}
- cancelAllAnimations();
+ cancelOverflowAnimations();
preparePopupContent();
mPopupWindow.update(x, y, getWidth(), getHeight());
}
@@ -563,10 +562,12 @@ public final class FloatingToolbar {
mHideAnimation.start();
}
- private void cancelAllAnimations() {
- mShowAnimation.cancel();
+ private void cancelDismissAndHideAnimations() {
mDismissAnimation.cancel();
mHideAnimation.cancel();
+ }
+
+ private void cancelOverflowAnimations() {
mOpenOverflowAnimation.cancel();
mCloseOverflowAnimation.cancel();
}
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 23b0d50c58d0..d86f71a4d1b9 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -584,6 +584,13 @@ static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject th
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 @@ static JNINativeMethod gMethods[] = {
"()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/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 8321ea4ad8d5..e511cc9d9931 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+ android:shareInterpolator="false" android:zAdjustment="top">
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
diff --git a/core/res/res/anim/wallpaper_close_enter.xml b/core/res/res/anim/wallpaper_close_enter.xml
index a18981393be0..7256a3c13b0e 100644
--- a/core/res/res/anim/wallpaper_close_enter.xml
+++ b/core/res/res/anim/wallpaper_close_enter.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+ android:shareInterpolator="false" android:zAdjustment="top">
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a5c6f84e1f36..00c771d8b14a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2329,7 +2329,7 @@
<!-- Defines whether this view reacts to long click events. -->
<attr name="longClickable" format="boolean" />
- <!-- If unset, no state will be saved for this view when it is being
+ <!-- If false, no state will be saved for this view when it is being
frozen. The default is true, allowing the view to be saved
(however it also must have an ID assigned to it for its
state to be saved). Setting this to false only disables the
@@ -2337,6 +2337,11 @@
be saved. -->
<attr name="saveEnabled" format="boolean" />
+ <!-- If true, no {@link android.view.ViewAssistStructure} data will be collected from
+ this view or any of its children. The default is false, allowing assist structure
+ to be reported by it. -->
+ <attr name="assistBlocked" format="boolean" />
+
<!-- Specifies whether to filter touches when the view's window is obscured by
another visible window. When set to true, the view will not receive touches
whenever a toast, dialog or other window appears above the view's window.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0b4320c45133..baccafd191ce 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2675,4 +2675,6 @@
<public type="attr" name="showForAllUsers" />
<!-- NFC CardEmulation: dynamically load service resources -->
<public type="attr" name="dynamicResources" />
+
+ <public type="attr" name="assistBlocked" />
</resources>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 279bfbf0f736..baa772e1c8e3 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 @@ public class PackageManagerTests extends AndroidTestCase {
}
}
- 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 5c9e8130e01d..ee9e2e4963dd 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.HOUR_IN_MILLIS;
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 @@ public class FileUtilsTest extends AndroidTestCase {
private File mDir;
private File mTestFile;
private File mCopyFile;
+ private File mTarget;
+
@Override
protected void setUp() throws Exception {
@@ -50,11 +53,15 @@ public class FileUtilsTest extends AndroidTestCase {
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 @@ public class FileUtilsTest extends AndroidTestCase {
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 e31290a51497..d674f7ff9356 100644
--- a/docs/html/google/play-services/index.jd
+++ b/docs/html/google/play-services/index.jd
@@ -88,7 +88,7 @@ On a wearable device, the swipe gesture is used to pan the map instead of exitin
<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"
+ <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"
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index cbfa82e7dafd..743e69233645 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -401,19 +401,35 @@ android.content.res.Resources#getQuantityString(int,int) getQuantityString}.</p>
format and style your string resources.</p>
-<h3>Escaping apostrophes and quotes</h3>
+<h3 id="escaping_quotes">Escaping apostrophes and quotes</h3>
-<p>If you have an apostrophe or a quote in your string, you must either escape it or enclose the
-whole string in the other type of enclosing quotes. For example, here are some stings that
-do and don't work:</p>
+<p>
+ If you have an apostrophe (<code>'</code>) in your string, you must either
+ escape it with a backslash (<code>\'</code>) or enclose the string in
+ double-quotes (<code>""</code>). For example, here are some strings that do
+ and don't work:
+</p>
<pre>
-&lt;string name="good_example">"This'll work"&lt;/string>
-&lt;string name="good_example_2">This\'ll also work&lt;/string>
+&lt;string name="good_example">This\'ll work&lt;/string>
+&lt;string name="good_example_2">"This'll also work"&lt;/string>
&lt;string name="bad_example">This doesn't work&lt;/string>
-&lt;string name="bad_example_2">XML encodings don&amp;apos;t work&lt;/string>
+ &lt;!-- Causes a compile error -->
</pre>
+<p>
+ If you have a double-quote in your string, you must escape it
+ (<code>\"</code>). Surrounding the string with single-quotes does
+ <em>not</em> work.
+</p>
+
+<pre>
+&lt;string name="good_example">This is a \"good string\".&lt;/string>
+&lt;string name="bad_example">This is a "bad string".&lt;/string>
+ &lt;!-- Quotes are stripped; displays as: This is a bad string. -->
+&lt;string name="bad_example_2">'This is another "bad string".'&lt;/string>
+ &lt;!-- Causes a compile error -->
+</pre>
<h3>Formatting strings</h3>
diff --git a/docs/html/reference/com/google/android/gms/fitness/data/Field.html b/docs/html/reference/com/google/android/gms/fitness/data/Field.html
index 91bf86113517..0f97e7e1538c 100644
--- a/docs/html/reference/com/google/android/gms/fitness/data/Field.html
+++ b/docs/html/reference/com/google/android/gms/fitness/data/Field.html
@@ -151,7 +151,7 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
<a name="top"></a>
-<!-- dialog to prompt lang pref change when loaded from hardcoded URL
+<!-- dialog to prompt lang pref change when loaded from hardcoded URL
<div id="langMessage" style="display:none">
<div>
<div class="lang en">
@@ -201,7 +201,7 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
<div id="header-wrapper">
<div id="header">
-
+
<div class="wrap" id="header-wrap">
@@ -245,8 +245,8 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
</ul>
-
-
+
+
<div class="menu-container">
<div class="moremenu">
<div id="more-btn"></div>
@@ -267,8 +267,8 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
<li><a href="http://source.android.com">Android Open Source Project</a></li>
</ul>
-
-
+
+
<div class="header">Language</div>
<div id="language" class="locales">
<select name="language" onChange="changeLangPref(this.value, true)">
@@ -286,8 +286,8 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
loadLangPref();
//-->
</script>
-
-
+
+
<br class="clearfix" />
</div><!-- end 'mid' -->
<div class="bottom"></div>
@@ -401,10 +401,10 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
</li>
<li><a href="/google/index.html">Google Services</a>
</li>
-
+
<li><a href="/samples/index.html">Samples</a>
</li>
-
+
</ul>
</li>
<li class="distribute last">
@@ -424,14 +424,14 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
</div><!-- end header-wrap.wrap -->
</div><!-- end header -->
-
+
<!-- Secondary x-nav -->
<div id="nav-x">
<div class="wrap" style="position:relative;z-index:1">
-
-
-
+
+
+
<ul class="nav-x col-9 develop" style="width:100%">
<li class="training"><a href="/training/index.html"
@@ -469,17 +469,17 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
<li class="google"><a href="/google/index.html"
>Google Services</a>
</li>
-
+
<li class="samples"><a href="/samples/index.html"
>Samples</a>
</li>
-
+
</ul>
</div>
</div>
<!-- /Sendondary x-nav DEVELOP -->
-
+
<div id="searchResults" class="wrap" style="display:none;">
<h2 id="searchTitle">Results</h2>
@@ -492,7 +492,7 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
<a class="logo" href="#top"></a>
<a class="top" href="#top"></a>
<ul class="breadcrumb">
-
+
<li class="current">Field</li>
</ul>
</div>
@@ -502,7 +502,7 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
-
+
<div class="wrap clearfix" id="body-content">
<div class="col-4" id="side-nav" itemscope itemtype="http://schema.org/SiteNavigationElement">
<div id="devdoc-nav" class="scroll-pane">
@@ -759,12 +759,12 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
</script>
-
+
</div>
<script type="text/javascript">
showGoogleRefTree();
-
+
</script>
</div> <!-- end side-nav -->
<script>
@@ -774,7 +774,7 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
</script>
-
+
@@ -784,21 +784,21 @@ href="//fonts.googleapis.com/css?family=Roboto+Condensed">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<div class="sum-details-links">
@@ -810,22 +810,22 @@ Summary:
<a href="#constants">Constants</a>
-
+
&#124; <a href="#inhconstants">Inherited Constants</a>
-
+
&#124; <a href="#lfields">Fields</a>
-
+
&#124; <a href="#pubmethods">Methods</a>
-
+
@@ -835,9 +835,9 @@ Summary:
</div><!-- end sum-details-links -->
<div class="api-level">
-
-
-
+
+
+
</div>
</div><!-- end api-info-block -->
@@ -847,31 +847,31 @@ Summary:
<div id="jd-header">
public
-
- final
-
+
+ final
+
class
<h1 itemprop="name">Field</h1>
-
+
extends Object<br/>
-
-
-
-
-
- implements
-
- Parcelable
-
-
-
-
+
+
+
+ implements
+
+ Parcelable
+
+
+
+
+
+
</div><!-- end header -->
@@ -883,18 +883,18 @@ Summary:
<tr>
-
+
<td colspan="2" class="jd-inheritance-class-cell">java.lang.Object</td>
</tr>
-
+
<tr>
-
+
<td class="jd-inheritance-space">&nbsp;&nbsp;&nbsp;&#x21b3;</td>
-
+
<td colspan="1" class="jd-inheritance-class-cell">com.google.android.gms.fitness.data.Field</td>
</tr>
-
+
</table>
@@ -962,31 +962,31 @@ Summary:
<table id="constants" class="jd-sumtable"><tr><th colspan="12">Constants</th></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_FLOAT">FORMAT_FLOAT</a></td>
<td class="jd-descrcol" width="100%">
Format constant indicating the field holds float values.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_INT32">FORMAT_INT32</a></td>
<td class="jd-descrcol" width="100%">
Format constant indicating the field holds integer values.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FORMAT_MAP">FORMAT_MAP</a></td>
@@ -1305,33 +1305,33 @@ android.os.Parcelable
</div>
<div id="inherited-constants-android.os.Parcelable-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol">CONTENTS_FILE_DESCRIPTOR</td>
<td class="jd-descrcol" width="100%">
-
-
-
+
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol">int</td>
<td class="jd-linkcol">PARCELABLE_WRITE_RETURN_VALUE</td>
<td class="jd-descrcol" width="100%">
-
-
-
+
+
+
</td>
</tr>
-
-
+
+
</table>
</div>
</div>
@@ -1347,7 +1347,7 @@ android.os.Parcelable
<table id="lfields" class="jd-sumtable"><tr><th colspan="12">Fields</th></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1357,13 +1357,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ACCURACY">FIELD_ACCURACY</a></td>
<td class="jd-descrcol" width="100%">
The accuracy of an accompanied value (such as location).
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1373,13 +1373,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ACTIVITY">FIELD_ACTIVITY</a></td>
<td class="jd-descrcol" width="100%">
An activity type of <code><a href="/reference/com/google/android/gms/fitness/FitnessActivities.html">FitnessActivities</a></code>, encoded as an integer for efficiency.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1389,13 +1389,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_ALTITUDE">FIELD_ALTITUDE</a></td>
<td class="jd-descrcol" width="100%">
An altitude of a location represented as a float, in meters above sea level.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1405,13 +1405,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_AVERAGE">FIELD_AVERAGE</a></td>
<td class="jd-descrcol" width="100%">
An average value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1421,13 +1421,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_BPM">FIELD_BPM</a></td>
<td class="jd-descrcol" width="100%">
A heart rate in beats per minute.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1437,13 +1437,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_CALORIES">FIELD_CALORIES</a></td>
<td class="jd-descrcol" width="100%">
Calories in kcal.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1469,13 +1469,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_CONFIDENCE">FIELD_CONFIDENCE</a></td>
<td class="jd-descrcol" width="100%">
The confidence of an accompanied value, specified as a value between 0.0 and 100.0.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1485,13 +1485,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_DISTANCE">FIELD_DISTANCE</a></td>
<td class="jd-descrcol" width="100%">
A distance in meters.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1501,13 +1501,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_DURATION">FIELD_DURATION</a></td>
<td class="jd-descrcol" width="100%">
A duration in milliseconds.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1533,13 +1533,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HEIGHT">FIELD_HEIGHT</a></td>
<td class="jd-descrcol" width="100%">
A height in meters.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1549,13 +1549,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HIGH_LATITUDE">FIELD_HIGH_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A high latitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1565,13 +1565,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_HIGH_LONGITUDE">FIELD_HIGH_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A high longitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1581,13 +1581,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LATITUDE">FIELD_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A latitude of a location represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1597,13 +1597,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LONGITUDE">FIELD_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A longitude of a location represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1613,13 +1613,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LOW_LATITUDE">FIELD_LOW_LATITUDE</a></td>
<td class="jd-descrcol" width="100%">
A low latitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1629,13 +1629,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_LOW_LONGITUDE">FIELD_LOW_LONGITUDE</a></td>
<td class="jd-descrcol" width="100%">
A low longitude of a location bounding box represented as a float, in degrees.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1645,13 +1645,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_MAX">FIELD_MAX</a></td>
<td class="jd-descrcol" width="100%">
A maximum value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1677,13 +1677,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_MIN">FIELD_MIN</a></td>
<td class="jd-descrcol" width="100%">
A minimum value.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1725,13 +1725,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_PERCENTAGE">FIELD_PERCENTAGE</a></td>
<td class="jd-descrcol" width="100%">
A percentage value, between 0 and 100.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1741,13 +1741,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_REVOLUTIONS">FIELD_REVOLUTIONS</a></td>
<td class="jd-descrcol" width="100%">
A count of revolutions.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1757,13 +1757,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_RPM">FIELD_RPM</a></td>
<td class="jd-descrcol" width="100%">
Revolutions per minute or rate per minute.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1773,13 +1773,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_SPEED">FIELD_SPEED</a></td>
<td class="jd-descrcol" width="100%">
A speed in meter/sec.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1789,13 +1789,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_STEPS">FIELD_STEPS</a></td>
<td class="jd-descrcol" width="100%">
A count of steps.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1805,13 +1805,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_WATTS">FIELD_WATTS</a></td>
<td class="jd-descrcol" width="100%">
Power in watts.
-
-
+
+
</td>
</tr>
-
-
+
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
public
@@ -1821,13 +1821,13 @@ android.os.Parcelable
<td class="jd-linkcol"><a href="/reference/com/google/android/gms/fitness/data/Field.html#FIELD_WEIGHT">FIELD_WEIGHT</a></td>
<td class="jd-descrcol" width="100%">
A weight in kilograms.
-
-
+
+
</td>
</tr>
-
-
+
+
</table>
@@ -1846,129 +1846,129 @@ android.os.Parcelable
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#describeContents()">describeContents</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
boolean</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#equals(java.lang.Object)">equals</a></span>(Object that)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#getFormat()">getFormat</a></span>()</nobr>
-
+
<div class="jd-descrdiv">
Returns the format of the field, as one of the format constant values.
-
-
+
+
</div>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#getName()">getName</a></span>()</nobr>
-
+
<div class="jd-descrdiv">
Returns the name of the field.
-
-
+
+
</div>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#hashCode()">hashCode</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#toString()">toString</a></span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad"><a href="/reference/com/google/android/gms/fitness/data/Field.html#writeToParcel(android.os.Parcel, int)">writeToParcel</a></span>(Parcel dest, int flags)</nobr>
-
+
</td></tr>
@@ -2003,182 +2003,182 @@ From class
</div>
<div id="inherited-methods-java.lang.Object-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
-
+
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
Object</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">clone</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
boolean</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">equals</span>(Object arg0)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">finalize</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
Class&lt;?&gt;</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">getClass</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
int</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">hashCode</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">notify</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">notifyAll</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
-
-
-
+
+
+
+
+
String</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">toString</span>()</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>()</nobr>
-
+
</td></tr>
-
+
<tr class=" api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>(long arg0, int arg1)</nobr>
-
+
</td></tr>
-
+
<tr class="alt-color api apilevel-" >
<td class="jd-typecol"><nobr>
-
-
+
+
final
-
-
+
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
<span class="sympad">wait</span>(long arg0)</nobr>
-
+
</td></tr>
@@ -2205,7 +2205,7 @@ From interface
</div>
<div id="inherited-methods-android.os.Parcelable-summary" style="display: none;">
<table class="jd-sumtable-expando">
-
+
@@ -2232,6 +2232,7 @@ From interface
+
void</nobr>
</td>
<td class="jd-linkcol" width="100%"><nobr>
@@ -3196,40 +3197,40 @@ From interface
<A NAME="NUTRIENT_TOTAL_FAT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_TOTAL_FAT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Total fat in grams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
-
+
"fat.total"
-
+
</span>
</div>
-
+
</div>
</div>
@@ -3237,40 +3238,40 @@ From interface
<A NAME="NUTRIENT_TRANS_FAT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_TRANS_FAT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Trans fat in grams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
-
+
"fat.trans"
-
+
</span>
</div>
-
+
</div>
</div>
@@ -3321,31 +3322,31 @@ From interface
<A NAME="NUTRIENT_VITAMIN_C"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
String
</span>
NUTRIENT_VITAMIN_C
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Vitamin C amount in milligrams.
</p></div>
-
+
<div class="jd-tagdata">
<span class="jd-tagtitle">Constant Value: </span>
<span>
@@ -3372,31 +3373,31 @@ From interface
<A NAME="FIELD_ACCURACY"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ACCURACY
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>The accuracy of an accompanied value (such as location).
</p></div>
-
+
</div>
</div>
@@ -3404,33 +3405,33 @@ From interface
<A NAME="FIELD_ACTIVITY"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ACTIVITY
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An activity type of <code><a href="/reference/com/google/android/gms/fitness/FitnessActivities.html">FitnessActivities</a></code>, encoded as an integer for efficiency. The
activity value should be stored using <code><a href="/reference/com/google/android/gms/fitness/data/Value.html#setActivity(java.lang.String)">setActivity(String)</a></code>,
and read using <code><a href="/reference/com/google/android/gms/fitness/data/Value.html#asActivity()">asActivity()</a></code>
</p></div>
-
+
</div>
</div>
@@ -3438,32 +3439,32 @@ From interface
<A NAME="FIELD_ALTITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_ALTITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An altitude of a location represented as a float, in meters above sea level.
Some location samples don't have an altitude value so this field might not be set.
</p></div>
-
+
</div>
</div>
@@ -3471,31 +3472,31 @@ From interface
<A NAME="FIELD_AVERAGE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_AVERAGE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>An average value.
</p></div>
-
+
</div>
</div>
@@ -3503,31 +3504,31 @@ From interface
<A NAME="FIELD_BPM"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_BPM
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A heart rate in beats per minute.
</p></div>
-
+
</div>
</div>
@@ -3535,31 +3536,31 @@ From interface
<A NAME="FIELD_CALORIES"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_CALORIES
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Calories in kcal.
</p></div>
-
+
</div>
</div>
@@ -3599,31 +3600,31 @@ From interface
<A NAME="FIELD_CONFIDENCE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_CONFIDENCE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>The confidence of an accompanied value, specified as a value between 0.0 and 100.0.
</p></div>
-
+
</div>
</div>
@@ -3631,31 +3632,31 @@ From interface
<A NAME="FIELD_DISTANCE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_DISTANCE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A distance in meters.
</p></div>
-
+
</div>
</div>
@@ -3663,31 +3664,31 @@ From interface
<A NAME="FIELD_DURATION"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_DURATION
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A duration in milliseconds.
</p></div>
-
+
</div>
</div>
@@ -3727,31 +3728,31 @@ From interface
<A NAME="FIELD_HEIGHT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HEIGHT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A height in meters.
</p></div>
-
+
</div>
</div>
@@ -3759,31 +3760,31 @@ From interface
<A NAME="FIELD_HIGH_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HIGH_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A high latitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3791,31 +3792,31 @@ From interface
<A NAME="FIELD_HIGH_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_HIGH_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A high longitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3823,31 +3824,31 @@ From interface
<A NAME="FIELD_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A latitude of a location represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3855,31 +3856,31 @@ From interface
<A NAME="FIELD_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A longitude of a location represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3887,31 +3888,31 @@ From interface
<A NAME="FIELD_LOW_LATITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LOW_LATITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A low latitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3919,31 +3920,31 @@ From interface
<A NAME="FIELD_LOW_LONGITUDE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_LOW_LONGITUDE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A low longitude of a location bounding box represented as a float, in degrees.
</p></div>
-
+
</div>
</div>
@@ -3951,31 +3952,31 @@ From interface
<A NAME="FIELD_MAX"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_MAX
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A maximum value.
</p></div>
-
+
</div>
</div>
@@ -4015,31 +4016,31 @@ From interface
<A NAME="FIELD_MIN"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_MIN
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A minimum value.
</p></div>
-
+
</div>
</div>
@@ -4113,31 +4114,31 @@ From interface
<A NAME="FIELD_PERCENTAGE"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_PERCENTAGE
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A percentage value, between 0 and 100.
</p></div>
-
+
</div>
</div>
@@ -4145,31 +4146,31 @@ From interface
<A NAME="FIELD_REVOLUTIONS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_REVOLUTIONS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A count of revolutions.
</p></div>
-
+
</div>
</div>
@@ -4177,31 +4178,31 @@ From interface
<A NAME="FIELD_RPM"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_RPM
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Revolutions per minute or rate per minute.
</p></div>
-
+
</div>
</div>
@@ -4209,31 +4210,31 @@ From interface
<A NAME="FIELD_SPEED"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_SPEED
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A speed in meter/sec.
</p></div>
-
+
</div>
</div>
@@ -4241,31 +4242,31 @@ From interface
<A NAME="FIELD_STEPS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_STEPS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A count of steps.
</p></div>
-
+
</div>
</div>
@@ -4273,31 +4274,31 @@ From interface
<A NAME="FIELD_WATTS"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_WATTS
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Power in watts.
</p></div>
-
+
</div>
</div>
@@ -4305,31 +4306,31 @@ From interface
<A NAME="FIELD_WEIGHT"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
- static
- final
+ public
+ static
+ final
<a href="/reference/com/google/android/gms/fitness/data/Field.html">Field</a>
</span>
FIELD_WEIGHT
</h4>
<div class="api-level">
-
-
-
+
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>A weight in kilograms.
</p></div>
-
+
</div>
</div>
@@ -4354,14 +4355,14 @@ From interface
<A NAME="describeContents()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">describeContents</span>
@@ -4369,15 +4370,15 @@ From interface
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4386,14 +4387,14 @@ From interface
<A NAME="equals(java.lang.Object)"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
boolean
</span>
<span class="sympad">equals</span>
@@ -4401,15 +4402,15 @@ From interface
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4418,14 +4419,14 @@ From interface
<A NAME="getFormat()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">getFormat</span>
@@ -4433,15 +4434,15 @@ From interface
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Returns the format of the field, as one of the format constant values.
</p></div>
@@ -4451,14 +4452,14 @@ From interface
<A NAME="getName()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
String
</span>
<span class="sympad">getName</span>
@@ -4466,15 +4467,15 @@ From interface
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p>Returns the name of the field.
</p></div>
@@ -4484,14 +4485,14 @@ From interface
<A NAME="hashCode()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
int
</span>
<span class="sympad">hashCode</span>
@@ -4499,15 +4500,15 @@ From interface
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4516,14 +4517,14 @@ From interface
<A NAME="toString()"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
String
</span>
<span class="sympad">toString</span>
@@ -4531,15 +4532,15 @@ From interface
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4548,14 +4549,14 @@ From interface
<A NAME="writeToParcel(android.os.Parcel, int)"></A>
-<div class="jd-details api apilevel-">
+<div class="jd-details api apilevel-">
<h4 class="jd-details-title">
<span class="normal">
- public
-
-
-
-
+ public
+
+
+
+
void
</span>
<span class="sympad">writeToParcel</span>
@@ -4563,15 +4564,15 @@ From interface
</h4>
<div class="api-level">
<div></div>
-
-
+
+
</div>
<div class="jd-details-descr">
-
-
-
+
+
+
<div class="jd-tagdata jd-tagdescr"><p></p></div>
</div>
@@ -4589,17 +4590,17 @@ From interface
<A NAME="navbar_top"></A>
<div id="footer" class="wrap" >
-
+
<div id="copyright">
-
+
Except as noted, this content is licensed under <a
- href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>.
+ href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>.
For details and restrictions, see the <a href="/license.html">
Content License</a>.
</div>
<div id="build_info">
-
+
<script src="/timestamp.js" type="text/javascript"></script>
<script>document.write(BUILD_TIMESTAMP)</script>
@@ -4607,7 +4608,7 @@ From interface
<div id="footerlinks">
-
+
<p>
<a href="/about/index.html">About Android</a>&nbsp;&nbsp;|&nbsp;
<a href="/legal.html">Legal</a>&nbsp;&nbsp;|&nbsp;
@@ -4620,7 +4621,7 @@ From interface
</div><!-- end doc-content -->
-</div> <!-- end body-content -->
+</div> <!-- end body-content -->
diff --git a/docs/html/training/enterprise/app-restrictions.jd b/docs/html/training/enterprise/app-restrictions.jd
index fc5dfcc9d0fd..dd2c2c015bce 100644
--- a/docs/html/training/enterprise/app-restrictions.jd
+++ b/docs/html/training/enterprise/app-restrictions.jd
@@ -316,8 +316,18 @@ if (!appCanUseCellular) {
{@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. Your app has to listen for
this intent so you can change the app's behavior when the restriction settings
- change. The following code shows how to dynamically register a broadcast
- receiver for this intent:
+ change.</p>
+
+<p class="note">
+ <strong>Note:</strong> The {@link
+ android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
+ ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent is sent only to listeners
+ that are dynamically registered, <em>not</em> to listeners that are declared
+ in the app manifest.
+</p>
+<p>
+ The following code shows how to dynamically register a broadcast receiver for
+ this intent:
</p>
<pre>IntentFilter restrictionsFilter =
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index c63c8ba91706..d6f8ccad2563 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -181,7 +181,7 @@ public class ImageFormat {
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 class ImageFormat {
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 @@ public class ImageFormat {
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 @@ public class ImageFormat {
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/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index 5fca8ec9a358..836f86875dcd 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -24,6 +24,7 @@ LOCAL_SRC_FILES := \
thread/TaskManager.cpp \
utils/Blur.cpp \
utils/GLUtils.cpp \
+ utils/LinearAllocator.cpp \
utils/SortedListImpl.cpp \
AmbientShadow.cpp \
AnimationContext.cpp \
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index c92ab91d21bf..f535afb2d61a 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -127,7 +127,7 @@ private:
}
void tryRecycleState(DeferredDisplayState* state) {
- mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState));
+ mAllocator.rewindIfLastAlloc(state);
}
/**
diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk
index 51898d2a1d22..b6f0baf4bf3e 100644
--- a/libs/hwui/tests/Android.mk
+++ b/libs/hwui/tests/Android.mk
@@ -34,16 +34,3 @@ LOCAL_SRC_FILES += \
tests/main.cpp
include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
-LOCAL_MODULE := hwui_unit_tests
-LOCAL_MODULE_TAGS := tests
-
-include $(LOCAL_PATH)/Android.common.mk
-
-LOCAL_SRC_FILES += \
- tests/ClipAreaTests.cpp \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk
new file mode 100644
index 000000000000..51601b072405
--- /dev/null
+++ b/libs/hwui/unit_tests/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+local_target_dir := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PATH:= $(call my-dir)/..
+
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
+LOCAL_MODULE := hwui_unit_tests
+LOCAL_MODULE_TAGS := tests
+
+include $(LOCAL_PATH)/Android.common.mk
+
+LOCAL_SRC_FILES += \
+ unit_tests/ClipAreaTests.cpp \
+ unit_tests/LinearAllocatorTests.cpp \
+ unit_tests/main.cpp
+
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp
index 166d5b6e92f6..166d5b6e92f6 100644
--- a/libs/hwui/tests/ClipAreaTests.cpp
+++ b/libs/hwui/unit_tests/ClipAreaTests.cpp
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
new file mode 100644
index 000000000000..b3959d169e1d
--- /dev/null
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <utils/LinearAllocator.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+struct SimplePair {
+ int one = 1;
+ int two = 2;
+};
+
+class SignalingDtor {
+public:
+ SignalingDtor() {
+ mDestroyed = nullptr;
+ }
+ SignalingDtor(bool* destroyedSignal) {
+ mDestroyed = destroyedSignal;
+ *mDestroyed = false;
+ }
+ virtual ~SignalingDtor() {
+ if (mDestroyed) {
+ *mDestroyed = true;
+ }
+ }
+ void setSignal(bool* destroyedSignal) {
+ mDestroyed = destroyedSignal;
+ }
+private:
+ bool* mDestroyed;
+};
+
+TEST(LinearAllocator, alloc) {
+ LinearAllocator la;
+ EXPECT_EQ(0u, la.usedSize());
+ la.alloc(64);
+ // There's some internal tracking as well as padding
+ // so the usedSize isn't strictly defined
+ EXPECT_LE(64u, la.usedSize());
+ EXPECT_GT(80u, la.usedSize());
+ auto pair = la.alloc<SimplePair>();
+ EXPECT_LE(64u + sizeof(SimplePair), la.usedSize());
+ EXPECT_GT(80u + sizeof(SimplePair), la.usedSize());
+ EXPECT_EQ(1, pair->one);
+ EXPECT_EQ(2, pair->two);
+}
+
+TEST(LinearAllocator, dtor) {
+ bool destroyed[10];
+ {
+ LinearAllocator la;
+ for (int i = 0; i < 5; i++) {
+ la.alloc<SignalingDtor>()->setSignal(destroyed + i);
+ la.alloc<SimplePair>();
+ }
+ la.alloc(100);
+ for (int i = 0; i < 5; i++) {
+ auto sd = new (la) SignalingDtor(destroyed + 5 + i);
+ la.autoDestroy(sd);
+ new (la) SimplePair();
+ }
+ la.alloc(100);
+ for (int i = 0; i < 10; i++) {
+ EXPECT_FALSE(destroyed[i]);
+ }
+ }
+ for (int i = 0; i < 10; i++) {
+ EXPECT_TRUE(destroyed[i]);
+ }
+}
+
+TEST(LinearAllocator, rewind) {
+ bool destroyed;
+ {
+ LinearAllocator la;
+ auto addr = la.alloc(100);
+ EXPECT_LE(100u, la.usedSize());
+ la.rewindIfLastAlloc(addr, 100);
+ EXPECT_GT(16u, la.usedSize());
+ size_t emptySize = la.usedSize();
+ auto sigdtor = la.alloc<SignalingDtor>();
+ sigdtor->setSignal(&destroyed);
+ EXPECT_FALSE(destroyed);
+ EXPECT_LE(emptySize, la.usedSize());
+ la.rewindIfLastAlloc(sigdtor);
+ EXPECT_TRUE(destroyed);
+ EXPECT_EQ(emptySize, la.usedSize());
+ destroyed = false;
+ }
+ // Checking for a double-destroy case
+ EXPECT_EQ(destroyed, false);
+}
diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt
new file mode 100755
index 000000000000..a2d6a34726df
--- /dev/null
+++ b/libs/hwui/unit_tests/how_to_run.txt
@@ -0,0 +1,4 @@
+mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests &&
+adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \
+ /data/nativetest/hwui_unit_tests/hwui_unit_tests &&
+adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests
diff --git a/libs/hwui/unit_tests/main.cpp b/libs/hwui/unit_tests/main.cpp
new file mode 100644
index 000000000000..c9b96360b36b
--- /dev/null
+++ b/libs/hwui/unit_tests/main.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
new file mode 100644
index 000000000000..59b12cf66a89
--- /dev/null
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_NDEBUG 1
+
+#include "utils/LinearAllocator.h"
+
+#include <stdlib.h>
+#include <utils/Log.h>
+
+
+// The ideal size of a page allocation (these need to be multiples of 8)
+#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
+#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
+
+// The maximum amount of wasted space we can have per page
+// Allocations exceeding this will have their own dedicated page
+// If this is too low, we will malloc too much
+// Too high, and we may waste too much space
+// Must be smaller than INITIAL_PAGE_SIZE
+#define MAX_WASTE_SIZE ((size_t)1024)
+
+#if ALIGN_DOUBLE
+#define ALIGN_SZ (sizeof(double))
+#else
+#define ALIGN_SZ (sizeof(int))
+#endif
+
+#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1))
+#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
+
+#if LOG_NDEBUG
+#define ADD_ALLOCATION(size)
+#define RM_ALLOCATION(size)
+#else
+#include <utils/Thread.h>
+#include <utils/Timers.h>
+static size_t s_totalAllocations = 0;
+static nsecs_t s_nextLog = 0;
+static android::Mutex s_mutex;
+
+static void _logUsageLocked() {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now > s_nextLog) {
+ s_nextLog = now + milliseconds_to_nanoseconds(10);
+ ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+ }
+}
+
+static void _addAllocation(size_t size) {
+ android::AutoMutex lock(s_mutex);
+ s_totalAllocations += size;
+ _logUsageLocked();
+}
+
+#define ADD_ALLOCATION(size) _addAllocation(size);
+#define RM_ALLOCATION(size) _addAllocation(-size);
+#endif
+
+#define min(x,y) (((x) < (y)) ? (x) : (y))
+
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) {
+ return la.alloc(size);
+}
+
+namespace android {
+namespace uirenderer {
+
+class LinearAllocator::Page {
+public:
+ Page* next() { return mNextPage; }
+ void setNext(Page* next) { mNextPage = next; }
+
+ Page()
+ : mNextPage(0)
+ {}
+
+ void* operator new(size_t /*size*/, void* buf) { return buf; }
+
+ void* start() {
+ return (void*) (((size_t)this) + sizeof(Page));
+ }
+
+ void* end(int pageSize) {
+ return (void*) (((size_t)start()) + pageSize);
+ }
+
+private:
+ Page(const Page& /*other*/) {}
+ Page* mNextPage;
+};
+
+LinearAllocator::LinearAllocator()
+ : mPageSize(INITIAL_PAGE_SIZE)
+ , mMaxAllocSize(MAX_WASTE_SIZE)
+ , mNext(0)
+ , mCurrentPage(0)
+ , mPages(0)
+ , mTotalAllocated(0)
+ , mWastedSpace(0)
+ , mPageCount(0)
+ , mDedicatedPageCount(0) {}
+
+LinearAllocator::~LinearAllocator(void) {
+ while (mDtorList) {
+ auto node = mDtorList;
+ mDtorList = node->next;
+ node->dtor(node->addr);
+ }
+ Page* p = mPages;
+ while (p) {
+ Page* next = p->next();
+ p->~Page();
+ free(p);
+ RM_ALLOCATION(mPageSize);
+ p = next;
+ }
+}
+
+void* LinearAllocator::start(Page* p) {
+ return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+}
+
+void* LinearAllocator::end(Page* p) {
+ return ((char*)p) + mPageSize;
+}
+
+bool LinearAllocator::fitsInCurrentPage(size_t size) {
+ return mNext && ((char*)mNext + size) <= end(mCurrentPage);
+}
+
+void LinearAllocator::ensureNext(size_t size) {
+ if (fitsInCurrentPage(size)) return;
+
+ if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
+ mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+ mPageSize = ALIGN(mPageSize);
+ }
+ mWastedSpace += mPageSize;
+ Page* p = newPage(mPageSize);
+ if (mCurrentPage) {
+ mCurrentPage->setNext(p);
+ }
+ mCurrentPage = p;
+ if (!mPages) {
+ mPages = mCurrentPage;
+ }
+ mNext = start(mCurrentPage);
+}
+
+void* LinearAllocator::alloc(size_t size) {
+ size = ALIGN(size);
+ if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
+ ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
+ // Allocation is too large, create a dedicated page for the allocation
+ Page* page = newPage(size);
+ mDedicatedPageCount++;
+ page->setNext(mPages);
+ mPages = page;
+ if (!mCurrentPage)
+ mCurrentPage = mPages;
+ return start(page);
+ }
+ ensureNext(size);
+ void* ptr = mNext;
+ mNext = ((char*)mNext) + size;
+ mWastedSpace -= size;
+ return ptr;
+}
+
+void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) {
+ static_assert(std::is_standard_layout<DestructorNode>::value,
+ "DestructorNode must have standard layout");
+ static_assert(std::is_trivially_destructible<DestructorNode>::value,
+ "DestructorNode must be trivially destructable");
+ auto node = new (*this) DestructorNode();
+ node->dtor = dtor;
+ node->addr = addr;
+ node->next = mDtorList;
+ mDtorList = node;
+}
+
+void LinearAllocator::runDestructorFor(void* addr) {
+ auto node = mDtorList;
+ DestructorNode* previous = nullptr;
+ while (node) {
+ if (node->addr == addr) {
+ if (previous) {
+ previous->next = node->next;
+ } else {
+ mDtorList = node->next;
+ }
+ node->dtor(node->addr);
+ rewindIfLastAlloc(node, sizeof(DestructorNode));
+ break;
+ }
+ previous = node;
+ node = node->next;
+ }
+}
+
+void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
+ // First run the destructor as running the destructor will
+ // also rewind for the DestructorNode allocation which will
+ // have been allocated after this void* if it has a destructor
+ runDestructorFor(ptr);
+ // Don't bother rewinding across pages
+ allocSize = ALIGN(allocSize);
+ if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
+ && ptr == ((char*)mNext - allocSize)) {
+ mWastedSpace += allocSize;
+ mNext = ptr;
+ }
+}
+
+LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
+ pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
+ ADD_ALLOCATION(pageSize);
+ mTotalAllocated += pageSize;
+ mPageCount++;
+ void* buf = malloc(pageSize);
+ return new (buf) Page();
+}
+
+static const char* toSize(size_t value, float& result) {
+ if (value < 2000) {
+ result = value;
+ return "B";
+ }
+ if (value < 2000000) {
+ result = value / 1024.0f;
+ return "KB";
+ }
+ result = value / 1048576.0f;
+ return "MB";
+}
+
+void LinearAllocator::dumpMemoryStats(const char* prefix) {
+ float prettySize;
+ const char* prettySuffix;
+ prettySuffix = toSize(mTotalAllocated, prettySize);
+ ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix);
+ prettySuffix = toSize(mWastedSpace, prettySize);
+ ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix,
+ (float) mWastedSpace / (float) mTotalAllocated * 100.0f);
+ ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
new file mode 100644
index 000000000000..d90dd825ea1d
--- /dev/null
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_LINEARALLOCATOR_H
+#define ANDROID_LINEARALLOCATOR_H
+
+#include <stddef.h>
+#include <type_traits>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids
+ * the overhead of malloc when many objects are allocated. It is most useful when creating many
+ * small objects with a similar lifetime, and doesn't add significant overhead for large
+ * allocations.
+ */
+class LinearAllocator {
+public:
+ LinearAllocator();
+ ~LinearAllocator();
+
+ /**
+ * Reserves and returns a region of memory of at least size 'size', aligning as needed.
+ * Typically this is used in an object's overridden new() method or as a replacement for malloc.
+ *
+ * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
+ * delete() on an object stored in a buffer is needed, it should be overridden to use
+ * rewindIfLastAlloc()
+ */
+ void* alloc(size_t size);
+
+ /**
+ * Allocates an instance of the template type with the default constructor
+ * and adds it to the automatic destruction list.
+ */
+ template<class T>
+ T* alloc() {
+ T* ret = new (*this) T;
+ autoDestroy(ret);
+ return ret;
+ }
+
+ /**
+ * Adds the pointer to the tracking list to have its destructor called
+ * when the LinearAllocator is destroyed.
+ */
+ template<class T>
+ void autoDestroy(T* addr) {
+ if (!std::is_trivially_destructible<T>::value) {
+ auto dtor = [](void* addr) { ((T*)addr)->~T(); };
+ addToDestructionList(dtor, addr);
+ }
+ }
+
+ /**
+ * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its
+ * state if possible.
+ */
+ void rewindIfLastAlloc(void* ptr, size_t allocSize);
+
+ /**
+ * Same as rewindIfLastAlloc(void*, size_t)
+ */
+ template<class T>
+ void rewindIfLastAlloc(T* ptr) {
+ rewindIfLastAlloc((void*)ptr, sizeof(T));
+ }
+
+ /**
+ * Dump memory usage statistics to the log (allocated and wasted space)
+ */
+ void dumpMemoryStats(const char* prefix = "");
+
+ /**
+ * The number of bytes used for buffers allocated in the LinearAllocator (does not count space
+ * wasted)
+ */
+ size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
+
+private:
+ LinearAllocator(const LinearAllocator& other);
+
+ class Page;
+ typedef void (*Destructor)(void* addr);
+ struct DestructorNode {
+ Destructor dtor;
+ void* addr;
+ DestructorNode* next = nullptr;
+ };
+
+ void addToDestructionList(Destructor, void* addr);
+ void runDestructorFor(void* addr);
+ Page* newPage(size_t pageSize);
+ bool fitsInCurrentPage(size_t size);
+ void ensureNext(size_t size);
+ void* start(Page *p);
+ void* end(Page* p);
+
+ size_t mPageSize;
+ size_t mMaxAllocSize;
+ void* mNext;
+ Page* mCurrentPage;
+ Page* mPages;
+ DestructorNode* mDtorList = nullptr;
+
+ // Memory usage tracking
+ size_t mTotalAllocated;
+ size_t mWastedSpace;
+ size_t mPageCount;
+ size_t mDedicatedPageCount;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la);
+
+#endif // ANDROID_LINEARALLOCATOR_H
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 8bbfb51fc8ad..2346abec81e9 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -20,6 +20,7 @@ import java.lang.annotation.Retention;
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.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Log;
/**
@@ -113,6 +115,11 @@ public class AudioRecord
*/
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 @@ public class AudioRecord
}
/**
- * 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 @@ public class AudioRecord
}
/**
- * 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 @@ public class AudioRecord
* 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 @@ public class AudioRecord
}
+ //--------------------------------------------------------------------------
+ // (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 @@ public class AudioRecord
}
+ //--------------------------------------------------------------------------
+ // 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 @@ public class AudioRecord
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 093ff26d0f86..847bdc290219 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,6 +23,7 @@ import java.lang.Math;
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.Message;
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;
@@ -176,6 +178,12 @@ public class AudioTrack
*/
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 @@ public class AudioTrack
/**
* 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 @@ public class AudioTrack
}
/**
- * 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 @@ public class AudioTrack
/**
* 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 class AudioTrack
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;
}
@@ -2082,33 +2112,93 @@ public class AudioTrack
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 +2227,10 @@ public class AudioTrack
* 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 +2278,55 @@ public class AudioTrack
}
}
+ /**
+ * 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 +2340,7 @@ public class AudioTrack
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 9ae468a2a4e4..75901fdb615a 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -86,6 +86,38 @@ public abstract class Image implements AutoCloseable {
* 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
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 3d8f9a07ce64..e54525dce948 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -130,7 +130,7 @@ public class ImageReader implements AutoCloseable {
* </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>
*
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index ff1b57d54aaf..974c9afa6c90 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -160,21 +160,21 @@ public final class MediaCodecInfo {
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 @@ public final class MediaCodecInfo {
* 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 @@ public final class MediaCodecInfo {
/** @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 @@ public final class MediaCodecInfo {
* 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 @@ public final class MediaCodecInfo {
* </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 @@ public final class MediaCodecInfo {
* 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 @@ public final class MediaCodecInfo {
/** @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 @@ public final class MediaCodecInfo {
// 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 @@ public final class MediaCodecInfo {
* <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 726622f95730..0e67daa5dade 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -232,11 +232,22 @@ public final class MediaFormat {
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 @@ public final class MediaFormat {
/**
* 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 541d871bd657..d6bf421ffa9f 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -43,6 +43,9 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
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 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
} 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 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
} 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 c1f1a7308616..3b4f8e5e5f7b 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -29,6 +29,7 @@ import android.view.Surface;
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;
@@ -586,8 +587,9 @@ final public class MediaSync {
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 +598,8 @@ final public class MediaSync {
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) {
diff --git a/media/java/android/media/OnAudioRecordRoutingListener.java b/media/java/android/media/OnAudioRecordRoutingListener.java
new file mode 100644
index 000000000000..8ff41c56fa93
--- /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 000000000000..18c72ef7377b
--- /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 c539290d0ab0..754facd1a4a2 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -32,11 +32,11 @@ import java.util.Objects;
* 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 +120,16 @@ import java.util.Objects;
* <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,10 +824,9 @@ public final class TvContentRating {
/**
* 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.
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 8c61129a069e..f5a6f2b41812 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.List;
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 @@ public final class TvContract {
* @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 @@ public final class TvContract {
* @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 @@ public final class TvContract {
* @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 final class TvContract {
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 @@ public final class TvContract {
* 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 @@ public final class TvContract {
/**
* 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 @@ public final class TvContract {
/**
* 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 @@ public final class TvContract {
/**
* 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 @@ public final class TvContract {
* }
* }
* </pre>
- * </p>
*/
public static final class Logo {
@@ -835,10 +815,9 @@ public final class TvContract {
/**
* 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 @@ public final class TvContract {
/**
* 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 @@ public final class TvContract {
* 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 @@ public final class TvContract {
/**
* 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 @@ public final class TvContract {
/**
* 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 @@ public final class TvContract {
/**
* 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 @@ public final class TvContract {
* 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 5c1193f30eb3..f0e2f5c5e0cf 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -295,18 +295,17 @@ public final class TvInputInfo implements Parcelable {
/**
* 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.
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 99d254e32b5a..105084e3fd3e 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.Rect;
import android.media.MediaPlayer;
@@ -149,15 +150,17 @@ public final class TvInputManager {
/**
* 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 +171,13 @@ public final class TvInputManager {
* <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>&lt;rating-system-definitions&gt;</code> that
* contains zero or more <code>&lt;rating-system-definition&gt;</code> elements. Each <code>
* &lt;rating-system-definition&gt;</code> element specifies the ratings, sub-ratings and rating
* orders of a particular content rating system.
- * </p>
*
* @see TvContentRating
*/
@@ -183,10 +186,9 @@ public final class TvInputManager {
/**
* 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 +231,7 @@ public final class TvInputManager {
* @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 +272,7 @@ public final class TvInputManager {
* @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 +373,11 @@ public final class TvInputManager {
/**
* 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,6 +931,7 @@ public final class TvInputManager {
* @param inputId The ID of the TV input.
* @return the {@link TvInputInfo} for a given TV input. {@code null} if not found.
*/
+ @Nullable
public TvInputInfo getTvInputInfo(String inputId) {
if (inputId == null) {
throw new IllegalArgumentException("inputId cannot be null");
@@ -1130,10 +1132,9 @@ public final class TvInputManager {
/**
* 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.
@@ -1501,7 +1502,7 @@ public final class TvInputManager {
* 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 +1551,7 @@ public final class TvInputManager {
* {@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 +1581,7 @@ public final class TvInputManager {
* @return the ID of the selected track.
* @see #selectTrack
*/
+ @Nullable
public String getSelectedTrack(int type) {
synchronized (mMetadataLock) {
if (type == TvTrackInfo.TYPE_AUDIO) {
@@ -1694,8 +1697,8 @@ public final class TvInputManager {
/**
* 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.
@@ -2113,7 +2116,7 @@ public final class TvInputManager {
/**
* 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 278d627a47c4..382ec91b5e80 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
@@ -58,15 +59,14 @@ import java.util.Set;
/**
* 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 +159,14 @@ public abstract class TvInputService extends Service {
/**
* 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 +177,7 @@ public abstract class TvInputService extends Service {
* @param hardwareInfo {@link TvInputHardwareInfo} object just added.
* @hide
*/
+ @Nullable
@SystemApi
public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) {
return null;
@@ -189,6 +191,7 @@ public abstract class TvInputService extends Service {
* @param hardwareInfo {@link TvInputHardwareInfo} object just removed.
* @hide
*/
+ @Nullable
@SystemApi
public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
return null;
@@ -202,6 +205,7 @@ public abstract class TvInputService extends Service {
* @param deviceInfo {@link HdmiDeviceInfo} object just added.
* @hide
*/
+ @Nullable
@SystemApi
public TvInputInfo onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
return null;
@@ -215,6 +219,7 @@ public abstract class TvInputService extends Service {
* @param deviceInfo {@link HdmiDeviceInfo} object just removed.
* @hide
*/
+ @Nullable
@SystemApi
public String onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
return null;
@@ -476,8 +481,8 @@ public abstract class TvInputService extends Service {
/**
* 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 @@ public abstract class TvInputService extends Service {
* 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 @@ public abstract class TvInputService extends Service {
/**
* 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,13 +533,12 @@ public abstract class TvInputService extends Service {
* 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
@@ -559,22 +562,21 @@ public abstract class TvInputService extends Service {
/**
* 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 +674,20 @@ public abstract class TvInputService extends Service {
/**
* 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 +700,15 @@ public abstract class TvInputService extends Service {
/**
* 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 +773,8 @@ public abstract class TvInputService extends Service {
/**
* 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 +784,13 @@ public abstract class TvInputService extends Service {
/**
* 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 +799,10 @@ public abstract class TvInputService extends Service {
/**
* 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 +812,7 @@ public abstract class TvInputService extends Service {
* @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;
}
@@ -883,10 +883,9 @@ public abstract class TvInputService extends Service {
/**
* 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 +905,12 @@ public abstract class TvInputService extends Service {
* 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 +940,11 @@ public abstract class TvInputService extends Service {
/**
* 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 +960,8 @@ public abstract class TvInputService extends Service {
* 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 +980,11 @@ public abstract class TvInputService extends Service {
* 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 +1000,9 @@ public abstract class TvInputService extends Service {
/**
* 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 +1425,8 @@ public abstract class TvInputService extends Service {
/**
* 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 +1434,10 @@ public abstract class TvInputService extends Service {
* 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 +1458,11 @@ public abstract class TvInputService extends Service {
/**
* 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 8394517a14a2..6eedeb43c36e 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -350,12 +350,11 @@ public final class TvTrackInfo implements Parcelable {
/**
* 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 fd5d64739f42..645604996d6f 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.graphics.Canvas;
@@ -52,14 +53,13 @@ import java.util.Queue;
* 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 +174,23 @@ public class TvView extends ViewGroup {
* @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
@@ -322,8 +321,8 @@ public class TvView extends ViewGroup {
/**
* 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 +343,8 @@ public class TvView extends ViewGroup {
/**
* 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 +359,8 @@ public class TvView extends ViewGroup {
/**
* 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 +471,7 @@ public class TvView extends ViewGroup {
* @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();
}
@@ -511,8 +509,8 @@ public class TvView extends ViewGroup {
/**
* 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 +795,11 @@ public class TvView extends ViewGroup {
/**
* 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 +960,8 @@ public class TvView extends ViewGroup {
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_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp
index 72dacdf6362d..f1922625ca0d 100644
--- a/media/jni/android_media_MediaSync.cpp
+++ b/media/jni/android_media_MediaSync.cpp
@@ -86,6 +86,10 @@ status_t JMediaSync::updateQueuedAudioData(
return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs);
}
+status_t JMediaSync::getPlayTimeForPendingAudioFrames(int64_t *outTimeUs) {
+ return mSync->getPlayTimeForPendingAudioFrames(outTimeUs);
+}
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -268,6 +272,21 @@ static jboolean android_media_MediaSync_native_getTimestamp(
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 @@ static JNINativeMethod gMethods[] = {
"(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 9e5de7e88090..cf81a72a1fbf 100644
--- a/media/jni/android_media_MediaSync.h
+++ b/media/jni/android_media_MediaSync.h
@@ -41,6 +41,8 @@ struct JMediaSync : public RefBase {
status_t setPlaybackRate(float rate);
+ status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs);
+
sp<const MediaClock> getMediaClock();
protected:
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 2f97809c1818..67d8ab6d8ce2 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -11,3 +11,5 @@ LOCAL_PACKAGE_NAME := DocumentsUI
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 062d433e67f2..528108777013 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -121,22 +121,20 @@
<string name="copy_remaining"><xliff:g id="duration" example="3 minutes">%s</xliff:g> left</string>
<!-- Toast shown when a file copy is kicked off -->
<plurals name="copy_begin">
- <item quantity="one">Copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
- <item quantity="other">Copying <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
+ <item quantity="one">Copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
+ <item quantity="other">Copying <xliff:g id="count" example="3">%1$d</xliff:g> files.</item>
</plurals>
<!-- Text shown on the copy notification while DocumentsUI performs setup in preparation for copying files [CHAR LIMIT=32] -->
<string name="copy_preparing">Preparing for copy\u2026</string>
<!-- Title of the copy error notification [CHAR LIMIT=48] -->
<plurals name="copy_error_notification_title">
- <item quantity="one">Error copying <xliff:g id="count" example="1">%1$d</xliff:g> file.</item>
- <item quantity="other">Error copying <xliff:g id="count" example="1">%1$d</xliff:g> files.</item>
+ <item quantity="one">Couldn\'t copy <xliff:g id="count" example="1">%1$d</xliff:g> file</item>
+ <item quantity="other">Couldn\'t copy <xliff:g id="count" example="2">%1$d</xliff:g> files</item>
</plurals>
<!-- Second line for notifications saying that more information will be shown after touching [CHAR LIMIT=48] -->
<string name="notification_touch_for_details">Touch to view details</string>
<!-- Label of a dialog button for retrying a failed operation [CHAR LIMIT=24] -->
<string name="retry">Retry</string>
- <!-- Title of the copying failure alert dialog. [CHAR LIMIT=48] -->
- <string name="copy_failure_alert_title">Error copying files</string>
<!-- Contents of the copying failure alert dialog. [CHAR LIMIT=48] -->
- <string name="copy_failure_alert_content">Following files are not copied: <xliff:g id="list">%1$s</xliff:g></string>
+ <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index a9f03b6b99e8..2e0bece2c752 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -26,6 +26,7 @@ import android.app.PendingIntent;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.CancellationSignal;
@@ -37,12 +38,14 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.text.format.DateUtils;
import android.util.Log;
+import android.widget.Toast;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import libcore.io.IoUtils;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -70,7 +73,7 @@ public class CopyService extends IntentService {
private volatile boolean mIsCancelled;
// Parameters of the copy job. Requests to an IntentService are serialized so this code only
// needs to deal with one job at a time.
- private final ArrayList<Uri> mFailedFiles;
+ private final ArrayList<DocumentInfo> mFailedFiles;
private long mBatchSize;
private long mBytesCopied;
private long mStartTime;
@@ -88,7 +91,27 @@ public class CopyService extends IntentService {
public CopyService() {
super("CopyService");
- mFailedFiles = new ArrayList<Uri>();
+ mFailedFiles = new ArrayList<DocumentInfo>();
+ }
+
+ /**
+ * Starts the service for a copy operation.
+ *
+ * @param context Context for the intent.
+ * @param srcDocs A list of src files to copy.
+ * @param dstStack The copy destination stack.
+ */
+ public static void start(Context context, List<DocumentInfo> srcDocs, DocumentStack dstStack) {
+ final Resources res = context.getResources();
+ final Intent copyIntent = new Intent(context, CopyService.class);
+ copyIntent.putParcelableArrayListExtra(
+ EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs));
+ copyIntent.putExtra(EXTRA_STACK, (Parcelable) dstStack);
+
+ Toast.makeText(context,
+ res.getQuantityString(R.plurals.copy_begin, srcDocs.size(), srcDocs.size()),
+ Toast.LENGTH_SHORT).show();
+ context.startService(copyIntent);
}
@Override
@@ -360,7 +383,7 @@ public class CopyService extends IntentService {
if (dstUri == null) {
// If this is a directory, the entire subdir will not be copied over.
Log.e(TAG, "Error while copying " + srcInfo.displayName);
- mFailedFiles.add(srcInfo.derivedUri);
+ mFailedFiles.add(srcInfo);
return;
}
@@ -444,7 +467,12 @@ public class CopyService extends IntentService {
} catch (IOException e) {
errorOccurred = true;
Log.e(TAG, "Error while copying " + srcUri.toString(), e);
- mFailedFiles.add(srcUri);
+ try {
+ mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri));
+ } catch (FileNotFoundException ignore) {
+ Log.w(TAG, "Source file gone: " + srcUri, e);
+ // The source file is gone.
+ }
} finally {
// This also ensures the file descriptors are closed.
IoUtils.closeQuietly(src);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 7cf58cc33e98..a789da86badd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -82,6 +82,7 @@ import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;
import com.google.android.collect.Lists;
@@ -341,9 +342,6 @@ public class DirectoryFragment extends Fragment {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- final Context context = getActivity();
- final Resources res = context.getResources();
-
// There's only one request code right now. Replace this with a switch statement or
// something more scalable when more codes are added.
if (requestCode != REQUEST_COPY_DESTINATION) {
@@ -355,15 +353,8 @@ public class DirectoryFragment extends Fragment {
return;
}
- final List<DocumentInfo> docs = getDisplayState(this).selectedDocumentsForCopy;
- final Intent copyIntent = new Intent(context, CopyService.class);
- copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(docs));
- copyIntent.putExtra(CopyService.EXTRA_STACK, data.getParcelableExtra(CopyService.EXTRA_STACK));
-
- Toast.makeText(context,
- res.getQuantityString(R.plurals.copy_begin, docs.size(), docs.size()),
- Toast.LENGTH_SHORT).show();
- context.startService(copyIntent);
+ CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
+ (DocumentStack) data.getParcelableExtra(CopyService.EXTRA_STACK));
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index 1748c9cb79a5..00b0f78c0fab 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
+import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.DialogInterface;
@@ -27,7 +28,9 @@ import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
+import com.android.documentsui.CopyService;
import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -40,9 +43,10 @@ public class FailureDialogFragment extends DialogFragment
private static final String TAG = "FailureDialogFragment";
private int mFailure;
- private ArrayList<Uri> mFailedSrcList;
+ private ArrayList<DocumentInfo> mFailedSrcList;
- public static void show(FragmentManager fm, int failure, ArrayList<Uri> failedSrcList) {
+ public static void show(FragmentManager fm, int failure,
+ ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack) {
// TODO: Add support for other failures than copy.
if (failure != CopyService.FAILURE_COPY) {
return;
@@ -62,7 +66,11 @@ public class FailureDialogFragment extends DialogFragment
@Override
public void onClick(DialogInterface dialog, int whichButton) {
- // TODO: Pass mFailure and mFailedSrcList to the parent fragment.
+ if (whichButton == DialogInterface.BUTTON_POSITIVE) {
+ CopyService.start(getActivity(), mFailedSrcList,
+ (DocumentStack) getActivity().getIntent().getParcelableExtra(
+ CopyService.EXTRA_STACK));
+ }
}
@Override
@@ -73,27 +81,17 @@ public class FailureDialogFragment extends DialogFragment
mFailedSrcList = getArguments().getParcelableArrayList(CopyService.EXTRA_SRC_LIST);
final StringBuilder list = new StringBuilder("<p>");
- for (Uri documentUri : mFailedSrcList) {
- try {
- final DocumentInfo documentInfo = DocumentInfo.fromUri(
- getActivity().getContentResolver(), documentUri);
- list.append(String.format("&#8226; %s<br>", documentInfo.displayName));
- }
- catch (FileNotFoundException ignore) {
- // Source file most probably gone.
- }
+ for (DocumentInfo documentInfo : mFailedSrcList) {
+ list.append(String.format("&#8226; %s<br>", documentInfo.displayName));
}
list.append("</p>");
final String message = String.format(getString(R.string.copy_failure_alert_content),
list.toString());
return new AlertDialog.Builder(getActivity())
- .setTitle(getString(R.string.copy_failure_alert_title))
.setMessage(Html.fromHtml(message))
- // TODO: Implement retrying the copy operation.
.setPositiveButton(R.string.retry, this)
.setNegativeButton(android.R.string.cancel, this)
- .setIcon(android.R.drawable.ic_dialog_alert)
.create();
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
index 1d021cb670b5..cad277d7dcf5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
@@ -106,12 +106,15 @@ public class StandaloneActivity extends BaseActivity {
RootsFragment.show(getFragmentManager(), null);
if (!mState.restored) {
new RestoreStackTask().execute();
+
+ // Show a failure dialog if there was a failed operation.
final Intent intent = getIntent();
+ final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
if (failure != 0) {
- final ArrayList<Uri> failedSrcList = intent.getParcelableArrayListExtra(
- CopyService.EXTRA_SRC_LIST);
- FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList);
+ final ArrayList<DocumentInfo> failedSrcList =
+ intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
+ FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList, dstStack);
}
} else {
onCurrentDirectoryChanged(ANIM_NONE);
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index aff57bf7ed79..73a723d0858e 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.util.Log;
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.IOException;
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 @@ public class ExternalStorageProvider extends DocumentsProvider {
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 @@ public class ExternalStorageProvider extends DocumentsProvider {
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 830731aac8e6..000000000000
--- 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 ffcd4998f1df..000000000000
--- 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 f980b60d54a6..000000000000
--- 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/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index 4fbcc1e54019..e083c9c7e1a2 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -18,6 +18,7 @@ package com.android.keyguard;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import android.content.Context;
import android.content.Intent;
@@ -141,7 +142,11 @@ public class CarrierText extends TextView {
plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
}
if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
- text = concatenate(plmn, spn);
+ if (Objects.equals(plmn, spn)) {
+ text = plmn;
+ } else {
+ text = concatenate(plmn, spn);
+ }
}
displayText = makeCarrierStringOnEmergencyCapable(
getContext().getText(R.string.keyguard_missing_sim_message_short), text);
@@ -293,11 +298,7 @@ public class CarrierText extends TextView {
final boolean plmnValid = !TextUtils.isEmpty(plmn);
final boolean spnValid = !TextUtils.isEmpty(spn);
if (plmnValid && spnValid) {
- if (plmn.equals(spn)) {
- return plmn;
- } else {
- return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
- }
+ return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
} else if (plmnValid) {
return plmn;
} else if (spnValid) {
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 000000000000..e0af29d3b92d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -0,0 +1,441 @@
+/*
+ * 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.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 {
+ /**
+ * 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 {
+ public 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);
+ }
+ }
+ }
+
+ // 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/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 0385d1eb02fd..8d99a6494db0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -387,6 +387,7 @@ final class SettingsState {
} catch (Throwable t) {
Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
destination.failWrite(out);
+ throw new IllegalStateException("Failed to write settings, restoring backup", t);
} finally {
IoUtils.closeQuietly(out);
}
@@ -408,10 +409,9 @@ final class SettingsState {
parser.setInput(in, null);
parseStateLocked(parser);
- // Any error while parsing is fatal.
- } catch (Throwable t) {
+ } catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed parsing settings file: "
- + mStatePersistFile , t);
+ + mStatePersistFile , e);
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
new file mode 100644
index 000000000000..ae0a362fee31
--- /dev/null
+++ b/packages/SystemUI/README.md
@@ -0,0 +1,5 @@
+# SystemUI Documentation
+
+---
+
+ * [Demo Mode](/packages/SystemUI/docs/demo_mode.md)
diff --git a/packages/SystemUI/docs/demo_mode.md b/packages/SystemUI/docs/demo_mode.md
new file mode 100644
index 000000000000..18ae4cbd7832
--- /dev/null
+++ b/packages/SystemUI/docs/demo_mode.md
@@ -0,0 +1,169 @@
+# Demo Mode for the Android System UI
+*Demo mode for the status bar allows you to force the status bar into a fixed state, useful for taking screenshots with a consistent status bar state, or testing different status icon permutations. Demo mode is available in recent versions of Android.*
+
+## Enabling demo mode
+Demo mode is protected behind a system setting. To enable it for a device, run:
+
+```
+adb shell settings put global sysui_demo_allowed 1
+```
+
+## Protocol
+The protocol is based on broadcast intents, and thus can be driven via the command line (```adb shell am broadcast```) or an app (```Context.sendBroadcast```).
+
+### Broadcast action
+```
+com.android.systemui.demo
+```
+
+### Commands
+Commands and subcommands (below) are sent as string extras in the broadcast
+intent.
+<br/>
+Commands are sent as string extras with key ```command``` (required). Possible values are:
+
+Command | Subcommand | Argument | Description
+--- | --- | --- | ---
+```enter``` | | | Enters demo mode, bar state allowed to be modified (for convenience, any of the other non-exit commands will automatically flip demo mode on, no need to call this explicitly in practice)
+```exit``` | | | Exits demo mode, bars back to their system-driven state
+```battery``` | | | Control the battery display
+ | ```level``` | | Sets the battery level (0 - 100)
+ | ```plugged``` | | Sets charging state (```true```, ```false```)
+```network``` | | | Control the RSSI display
+ | ```airplane``` | | ```show``` to show icon, any other value to hide
+ | ```fully``` | | Sets MCS state to fully connected (```true```, ```false```)
+ | ```wifi``` | | ```show``` to show icon, any other value to hide
+ | | ```level``` | Sets wifi level (null or 0-4)
+ | ```mobile``` | | ```show``` to show icon, any other value to hide
+ | | ```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide
+ | | ```level``` | Sets mobile signal strength level (null or 0-4)
+ | ```carriernetworkchange``` | | Sets mobile signal icon to carrier network change UX when disconnected (```show``` to show icon, any other value to hide)
+```bars``` | | | Control the visual style of the bars (opaque, translucent, etc)
+ | ```mode``` | | Sets the bars visual style (opaque, translucent, semi-transparent)
+```status``` | | | Control the system status icons
+ | ```volume``` | | Sets the icon in the volume slot (```silent```, ```vibrate```, any other value to hide)
+ | ```bluetooth``` | | Sets the icon in the bluetooth slot (```connected```, ```disconnected```, any other value to hide)
+ | ```location``` | | Sets the icon in the location slot (```show```, any other value to hide)
+ | ```alarm``` | | Sets the icon in the alarm_clock slot (```show```, any other value to hide)
+ | ```sync``` | | Sets the icon in the sync_active slot (```show```, any other value to hide)
+ | ```tty``` | | Sets the icon in the tty slot (```show```, any other value to hide)
+ | ```eri``` | | Sets the icon in the cdma_eri slot (```show```, any other value to hide)
+ | ```mute``` | | Sets the icon in the mute slot (```show```, any other value to hide)
+ | ```speakerphone``` | | Sets the icon in the speakerphone slot (```show```, any other value to hide)
+```notifications``` | | | Control the notification icons
+ | ```visible``` | | ```false``` to hide the notification icons, any other value to show
+```clock``` | | | Control the clock display
+ | ```millis``` | | Sets the time in millis
+ | ```hhmm``` | | Sets the time in hh:mm
+
+## Examples
+Enter demo mode
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command enter
+```
+
+
+Exit demo mode
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command exit
+```
+
+
+Set the clock to 12:31
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm
+1231
+```
+
+
+Set the wifi level to max
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi
+show -e level 4
+```
+
+
+Show the silent volume icon
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command status -e volume
+silent
+```
+
+
+Empty battery, and not charging (red exclamation point)
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command battery -e level
+0 -e plugged false
+```
+
+
+Hide the notification icons
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command notifications -e
+visible false
+```
+
+
+Exit demo mode
+
+```
+adb shell am broadcast -a com.android.systemui.demo -e command exit
+```
+
+
+## Example demo controller app in AOSP
+```
+frameworks/base/tests/SystemUIDemoModeController
+```
+
+
+## Example script (for screenshotting purposes)
+```bash
+#!/bin/sh
+CMD=$1
+
+if [[ $ADB == "" ]]; then
+ ADB=adb
+fi
+
+if [[ $CMD != "on" && $CMD != "off" ]]; then
+ echo "Usage: $0 [on|off] [hhmm]" >&2
+ exit
+fi
+
+if [[ "$2" != "" ]]; then
+ HHMM="$2"
+fi
+
+$ADB root || exit
+$ADB wait-for-devices
+$ADB shell settings put global sysui_demo_allowed 1
+
+if [ $CMD == "on" ]; then
+ $ADB shell am broadcast -a com.android.systemui.demo -e command enter || exit
+ if [[ "$HHMM" != "" ]]; then
+ $ADB shell am broadcast -a com.android.systemui.demo -e command clock -e
+hhmm ${HHMM}
+ fi
+ $ADB shell am broadcast -a com.android.systemui.demo -e command battery -e
+plugged false
+ $ADB shell am broadcast -a com.android.systemui.demo -e command battery -e
+level 100
+ $ADB shell am broadcast -a com.android.systemui.demo -e command network -e
+wifi show -e level 4
+ $ADB shell am broadcast -a com.android.systemui.demo -e command network -e
+mobile show -e datatype none -e level 4
+ $ADB shell am broadcast -a com.android.systemui.demo -e command notifications
+-e visible false
+elif [ $CMD == "off" ]; then
+ $ADB shell am broadcast -a com.android.systemui.demo -e command exit
+fi
+```
+
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ccbd0a65a4af..6e59029f08bb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -570,4 +570,7 @@
<!-- Padding to be used on the bottom of the fingerprint icon on Keyguard so it better aligns
with the other icons. -->
<dimen name="fingerprint_icon_additional_padding">12dp</dimen>
+
+ <!-- Minimum margin of the notification panel on the side, when being positioned dynamically -->
+ <dimen name="notification_panel_min_side_margin">48dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 03e5746e994f..7d61099066d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -187,6 +187,9 @@ public class NotificationPanelView extends PanelView implements
private boolean mExpansionIsFromHeadsUp;
private int mBottomBarHeight;
private boolean mExpandingFromHeadsUp;
+ private int mPositionMinSideMargin;
+ private int mLastOrientation = -1;
+
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
public void run() {
@@ -236,6 +239,7 @@ public class NotificationPanelView extends PanelView implements
mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
mSecureCameraLaunchManager =
new SecureCameraLaunchManager(getContext(), mKeyguardBottomArea);
+ mLastOrientation = getResources().getConfiguration().orientation;
// recompute internal state when qspanel height changes
mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@@ -268,6 +272,8 @@ public class NotificationPanelView extends PanelView implements
getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
mQsFalsingThreshold = getResources().getDimensionPixelSize(
R.dimen.qs_falsing_threshold);
+ mPositionMinSideMargin = getResources().getDimensionPixelSize(
+ R.dimen.notification_panel_min_side_margin);
}
public void updateResources() {
@@ -692,6 +698,9 @@ public class NotificationPanelView extends PanelView implements
if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) {
return true;
}
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
+ updateVerticalPanelPosition(event.getX());
+ }
super.onTouchEvent(event);
return true;
}
@@ -740,7 +749,7 @@ public class NotificationPanelView extends PanelView implements
}
private boolean isInQsArea(float x, float y) {
- return (x >= mScrollView.getLeft() && x <= mScrollView.getRight()) &&
+ return (x >= mScrollView.getX() && x <= mScrollView.getX() + mScrollView.getWidth()) &&
(y <= mNotificationStackScroller.getBottomMostNotificationBottom()
|| y <= mQsContainer.getY() + mQsContainer.getHeight());
}
@@ -939,7 +948,7 @@ public class NotificationPanelView extends PanelView implements
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
}
-
+ resetVerticalPanelPosition();
updateQsState();
}
@@ -1376,7 +1385,7 @@ public class NotificationPanelView extends PanelView implements
return false;
}
View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader;
- boolean onHeader = x >= header.getLeft() && x <= header.getRight()
+ boolean onHeader = x >= header.getX() && x <= header.getX() + header.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0) && isInQsArea(x, y);
@@ -1771,6 +1780,10 @@ public class NotificationPanelView extends PanelView implements
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mAfforanceHelper.onConfigurationChanged();
+ if (newConfig.orientation != mLastOrientation) {
+ resetVerticalPanelPosition();
+ }
+ mLastOrientation = newConfig.orientation;
}
@Override
@@ -2185,4 +2198,42 @@ public class NotificationPanelView extends PanelView implements
mExpandingFromHeadsUp = true;
}
}
+
+ @Override
+ protected void onClosingFinished() {
+ super.onClosingFinished();
+ resetVerticalPanelPosition();
+ }
+
+ /**
+ * Updates the vertical position of the panel so it is positioned closer to the touch
+ * responsible for opening the panel.
+ *
+ * @param x the x-coordinate the touch event
+ */
+ private void updateVerticalPanelPosition(float x) {
+ if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) {
+ resetVerticalPanelPosition();
+ return;
+ }
+ float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
+ float rightMost = getWidth() - mPositionMinSideMargin
+ - mNotificationStackScroller.getWidth() / 2;
+ if (Math.abs(x - getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
+ x = getWidth() / 2;
+ }
+ x = Math.min(rightMost, Math.max(leftMost, x));
+ setVerticalPanelTranslation(x -
+ (mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth()/2));
+ }
+
+ private void resetVerticalPanelPosition() {
+ setVerticalPanelTranslation(0f);
+ }
+
+ private void setVerticalPanelTranslation(float translation) {
+ mNotificationStackScroller.setTranslationX(translation);
+ mScrollView.setTranslationX(translation);
+ mHeader.setTranslationX(translation);
+ }
}
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 b6dbfceb8e2d..c854d63b930f 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
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 194a19afabb7..0caf51af9807 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 @@ public class StatusBarKeyguardViewManager {
if (mShowing) {
if (mOccluded) {
mPhoneStatusBar.hideKeyguard();
+ mPhoneStatusBar.stopWaitingForKeyguardExit();
mBouncer.hide(false /* destroyView */);
} else {
showBouncerOrKeyguard();
diff --git a/preloaded-classes b/preloaded-classes
index 86bd5c9ef9ec..95d0b4276c9b 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -828,6 +828,11 @@ android.hardware.soundtrigger.SoundTriggerModule
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/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 5d025764704b..0e3867d6ff5c 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -566,13 +566,22 @@ class AlarmManagerService extends SystemService {
}
void restorePendingWhileIdleAlarmsLocked() {
+ // Bring pending alarms back into the main list.
final long nowElapsed = SystemClock.elapsedRealtime();
- for (int i=mPendingWhileIdleAlarms.size() - 1; i >= 0 && mPendingIdleUntil != null; i --) {
+ for (int i=mPendingWhileIdleAlarms.size() - 1; i >= 0 && mPendingIdleUntil == null; i--) {
Alarm a = mPendingWhileIdleAlarms.remove(i);
reAddAlarmLocked(a, nowElapsed, false);
}
+
+ // Reschedule everything.
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
+
+ // And send a TIME_TICK right now, since it is important to get the UI updated.
+ try {
+ mTimeTickSender.send();
+ } catch (PendingIntent.CanceledException e) {
+ }
}
static final class InFlight extends Intent {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a31a1a7003f8..5df74c5edd05 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -220,6 +220,35 @@ public class LockSettingsService extends ILockSettings.Stub {
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 f88802a73f5c..89a717350798 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -232,7 +232,12 @@ class MountService extends IMountService.Stub
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 @@ class MountService extends IMountService.Stub
/** 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 @@ class MountService extends IMountService.Stub
}
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 @@ class MountService extends IMountService.Stub
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 @@ class MountService extends IMountService.Stub
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 @@ class MountService extends IMountService.Stub
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 @@ class MountService extends IMountService.Stub
}
@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();
@@ -2700,7 +2744,7 @@ class MountService extends IMountService.Stub
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 +2792,8 @@ class MountService extends IMountService.Stub
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 +2821,11 @@ class MountService extends IMountService.Stub
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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d5ae8e7be58..a48a4d903b6a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -437,6 +437,11 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
+ /**
+ * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
+ */
+ String mDeviceOwnerName;
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -4831,6 +4836,9 @@ public final class ActivityManagerService extends ActivityManagerNative
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
+ if (packageName != null && packageName.equals(mDeviceOwnerName)) {
+ throw new SecurityException("Clearing DeviceOwner data is forbidden.");
+ }
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
userId = handleIncomingUser(pid, uid,
@@ -8563,6 +8571,17 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public void updateDeviceOwner(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("updateDeviceOwner called from non-system process");
+ }
+ synchronized (this) {
+ mDeviceOwnerName = packageName;
+ }
+ }
+
+ @Override
public void updateLockTaskPackages(int userId, String[] packages) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 905adc0b02d9..58665d7fe35e 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -982,9 +982,18 @@ public final class BatteryStatsService extends IBatteryStats.Stub
if (noOutput) {
return;
}
- if (BatteryStatsHelper.checkWifiOnly(mContext)) {
- flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (BatteryStatsHelper.checkWifiOnly(mContext)) {
+ flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
+ }
+ // Fetch data from external sources and update the BatteryStatsImpl object with them.
+ updateExternalStats("dump");
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
+
if (reqUid >= 0) {
// By default, if the caller is only interested in a specific package, then
// we only dump the aggregated data since charged.
@@ -995,9 +1004,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
- // Fetch data from external sources and update the BatteryStatsImpl object with them.
- updateExternalStats("dump");
-
if (useCheckinFormat) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
if (isRealCheckin) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index c4f410f3c701..5ac027dc54ab 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 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@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/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4c36fa6eea65..a42e4e72a507 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -199,7 +199,8 @@ final class PackageDexOptimizer {
@Nullable
private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
throws IOException {
- if (pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) {
+ if ((pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) || pkg.isForwardLocked()
+ || pkg.applicationInfo.isExternalAsec()) {
return null;
}
File codePath = new File(pkg.codePath);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 89fa3202cd8e..a40617545995 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
} 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 24cc909ba705..bd225247bfb2 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_VERIFICATIO
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.Message;
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.PrintStreamPrinter;
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.app.ResolverActivity;
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.Map;
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 @@ public class PackageManagerService extends IPackageManager.Stub {
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 @@ public class PackageManagerService extends IPackageManager.Stub {
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(this);
+ mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
getDefaultDisplayMetrics(context, mMetrics);
@@ -11067,7 +11076,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
|| (args.volumeUuid != null));
boolean replace = false;
- final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
+ int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
// Result object to be returned
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
@@ -11234,13 +11243,18 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
- // Run dexopt before old package gets removed, to minimize time when app is not available
- int result = mPackageDexOptimizer
- .performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
- false /* defer */, false /* inclDependencies */);
- if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
- res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
- return;
+ // If app directory is not writable, dexopt will be called after the rename
+ if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+ // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
+ scanFlags |= SCAN_NO_DEX;
+ // Run dexopt before old package gets removed, to minimize time when app is unavailable
+ int result = mPackageDexOptimizer
+ .performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
+ false /* defer */, false /* inclDependencies */);
+ if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
+ res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
+ return;
+ }
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -11250,13 +11264,12 @@ public class PackageManagerService extends IPackageManager.Stub {
startIntentFilterVerifications(args.user.getIdentifier(), pkg);
- // Call with SCAN_NO_DEX, since dexopt has already been made
if (replace) {
- replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING | SCAN_NO_DEX, args.user,
+ replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, volumeUuid, res);
} else {
- installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES
- | SCAN_NO_DEX, args.user, installerPackageName, volumeUuid, res);
+ installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
+ args.user, installerPackageName, volumeUuid, res);
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
@@ -14133,49 +14146,25 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@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) {
- }
- }
- }
-
- @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) {
- }
+ mMoveCallbacks.notifyStatusChanged(moveId, PackageManager.MOVE_FAILED_INTERNAL_ERROR);
}
+ return moveId;
}
- 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;
@@ -14201,8 +14190,13 @@ public class PackageManagerService extends IPackageManager.Stub {
// 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;
@@ -14211,10 +14205,36 @@ public class PackageManagerService extends IPackageManager.Stub {
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) {
@@ -14230,6 +14250,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ mMoveCallbacks.notifyStatusChanged(moveId, 50);
+
final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) throws RemoteException {
@@ -14255,13 +14277,16 @@ public class PackageManagerService extends IPackageManager.Stub {
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;
}
}
@@ -14279,6 +14304,39 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@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);
@@ -14601,4 +14659,82 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ 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/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b3aa966e37a8..252c16a6fb87 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4275,6 +4275,8 @@ final class Settings {
Slog.wtf(PackageManagerService.TAG,
"Failed to write settings, restoring backup", t);
destination.failWrite(out);
+ throw new IllegalStateException("Failed to write runtime permissions,"
+ + " restoring backup", t);
} finally {
IoUtils.closeQuietly(out);
}
@@ -4322,10 +4324,9 @@ final class Settings {
parser.setInput(in, null);
parseRuntimePermissionsLPr(parser, userId);
- // Any error while parsing is fatal.
- } catch (Throwable t) {
+ } catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed parsing permissions file: "
- + permissionsFile , t);
+ + permissionsFile , e);
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4c06cbe326ac..9033c9cd26ec 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -501,12 +501,15 @@ public class AppTransition implements Dump {
return a;
}
- private Animation createClipRevealAnimationLocked(int transit, boolean enter,
- int appWidth, int appHeight) {
+ private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame) {
final Animation anim;
if (enter) {
// Reveal will expand and move faster in horizontal direction
+ // Start from upper left of start and move to final position
+ final int appWidth = appFrame.width();
+ final int appHeight = appFrame.height();
+
// Start from size of launch icon, expand to full width/height
Animation clipAnimLR = new ClipRectLRAnimation(
(appWidth - mNextAppTransitionStartWidth) / 2,
@@ -521,9 +524,9 @@ public class AppTransition implements Dump {
// Start from middle of launch icon area, move to 0, 0
int startMiddleX = mNextAppTransitionStartX +
- (mNextAppTransitionStartWidth - appWidth) / 2;
+ (mNextAppTransitionStartWidth - appWidth) / 2 - appFrame.left;
int startMiddleY = mNextAppTransitionStartY +
- (mNextAppTransitionStartHeight - appHeight) / 2;
+ (mNextAppTransitionStartHeight - appHeight) / 2 - appFrame.top;
TranslateXAnimation translateX = new TranslateXAnimation(
Animation.ABSOLUTE, startMiddleX, Animation.ABSOLUTE, 0);
@@ -940,7 +943,7 @@ public class AppTransition implements Dump {
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
- boolean isFullScreen, boolean isVoiceInteraction) {
+ Rect appFrame, boolean isFullScreen, boolean isVoiceInteraction) {
Animation a;
if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
@@ -977,9 +980,7 @@ public class AppTransition implements Dump {
+ " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
+ " transit=" + transit + " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
- a = createClipRevealAnimationLocked(transit, enter,
- containingFrame.right - containingFrame.left,
- containingFrame.bottom - containingFrame.top);
+ a = createClipRevealAnimationLocked(transit, enter, appFrame);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f29d524bce03..38fc95957291 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -624,6 +624,12 @@ public class WindowManagerService extends IWindowManager.Stub
static final int WALLPAPER_DRAW_TIMEOUT = 2;
int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
+ // Set to the wallpaper window we would like to hide once the transition animations are done.
+ // This is useful in cases where we don't want the wallpaper to be hidden when the close app
+ // is a wallpaper target and is done animating out, but the opening app isn't a wallpaper
+ // target and isn't done animating in.
+ WindowState mDeferredHideWallpaper = null;
+
AppWindowToken mFocusedApp = null;
PowerManager mPowerManager;
@@ -1781,17 +1787,24 @@ public class WindowManagerService extends IWindowManager.Stub
}
void hideWallpapersLocked(final WindowState winGoingAway) {
- if (mWallpaperTarget != null &&
- (mWallpaperTarget != winGoingAway || mLowerWallpaperTarget != null)) {
+ if (mWallpaperTarget != null
+ && (mWallpaperTarget != winGoingAway || mLowerWallpaperTarget != null)) {
+ return;
+ }
+ if (mAppTransition.isRunning()) {
+ // Defer hiding the wallpaper when app transition is running until the animations
+ // are done.
+ mDeferredHideWallpaper = winGoingAway;
return;
}
+ final boolean wasDeferred = (mDeferredHideWallpaper == winGoingAway);
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WindowToken token = mWallpaperTokens.get(i);
for (int j = token.windows.size() - 1; j >= 0; j--) {
final WindowState wallpaper = token.windows.get(j);
final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
- if (!winAnimator.mLastHidden) {
+ if (!winAnimator.mLastHidden || wasDeferred) {
winAnimator.hide();
dispatchWallpaperVisibility(wallpaper, false);
final DisplayContent displayContent = wallpaper.getDisplayContent();
@@ -2270,12 +2283,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
- * Check wallpaper for visiblity change and notify window if so.
+ * Check wallpaper for visibility change and notify window if so.
* @param wallpaper The wallpaper to test and notify.
* @param visible Current visibility.
*/
void dispatchWallpaperVisibility(final WindowState wallpaper, final boolean visible) {
- if (wallpaper.mWallpaperVisible != visible) {
+ // Only send notification if the visibility actually changed and we are not trying to hide
+ // the wallpaper when we are deferring hiding of the wallpaper.
+ if (wallpaper.mWallpaperVisible != visible
+ && (mDeferredHideWallpaper == null || visible)) {
wallpaper.mWallpaperVisible = visible;
try {
if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
@@ -3412,6 +3428,7 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState win = atoken.findMainWindow();
Rect containingFrame = new Rect(0, 0, width, height);
Rect contentInsets = new Rect();
+ Rect appFrame = new Rect(0, 0, width, height);
boolean isFullScreen = true;
if (win != null) {
if (win.mContainingFrame != null) {
@@ -3420,6 +3437,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.mContentInsets != null) {
contentInsets.set(win.mContentInsets);
}
+ if (win.mFrame != null) {
+ appFrame.set(win.mFrame);
+ }
isFullScreen =
((win.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) ==
SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) ||
@@ -3433,8 +3453,8 @@ public class WindowManagerService extends IWindowManager.Stub
enter = false;
}
Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
- mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen,
- isVoiceInteraction);
+ mCurConfiguration.orientation, containingFrame, contentInsets, appFrame,
+ isFullScreen, isVoiceInteraction);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
@@ -9523,6 +9543,12 @@ public class WindowManagerService extends IWindowManager.Stub
int changes = 0;
mAppTransition.setIdle();
+
+ if (mDeferredHideWallpaper != null) {
+ hideWallpapersLocked(mDeferredHideWallpaper);
+ mDeferredHideWallpaper = null;
+ }
+
// Restore window app tokens to the ActivityManager views
ArrayList<TaskStack> stacks = getDefaultDisplayContentLocked().getStacks();
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 16d27fa4d1ad..cd9689e46aa1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1635,6 +1635,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
pw.println();
pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
pw.print(" last="); mLastSystemDecorRect.printShortString(pw);
+ if (mWinAnimator.mHasClipRect) {
+ pw.print(" mLastClipRect=");
+ mWinAnimator.mLastClipRect.printShortString(pw);
+ }
pw.println();
}
if (mEnforceSizeCompat) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1a30cbaea43d..d6726c17cce4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1139,8 +1139,6 @@ class WindowStateAnimator {
mShownAlpha *= appTransformation.getAlpha();
if (appTransformation.hasClipRect()) {
mClipRect.set(appTransformation.getClipRect());
- // Account for non-fullscreen windows
- mClipRect.offset(frame.left, frame.top);
if (mWin.mHScale > 0) {
mClipRect.left /= mWin.mHScale;
mClipRect.right /= mWin.mHScale;
@@ -1223,7 +1221,7 @@ class WindowStateAnimator {
}
}
- void applyDecorRect(final Rect decorRect) {
+ private void applyDecorRect(final Rect decorRect) {
final WindowState w = mWin;
final int width = w.mFrame.width();
final int height = w.mFrame.height();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 80d075dd57fd..eb9234a1029e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -137,6 +137,7 @@ import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -304,7 +305,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
// This is the list of component allowed to start lock task mode.
- final List<String> mLockTaskPackages = new ArrayList<>();
+ List<String> mLockTaskPackages = new ArrayList<>();
boolean mStatusBarEnabledState = true;
@@ -1105,6 +1106,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
void loadDeviceOwner() {
synchronized (this) {
mDeviceOwner = DeviceOwner.load();
+ updateDeviceOwnerLocked();
}
}
@@ -1628,36 +1630,54 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// 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);
syncDeviceCapabilitiesLocked(policy);
updateMaximumTimeToLockLocked(policy);
- updateLockTaskPackagesLocked(policy, userHandle);
+ addDeviceInitializerToLockTaskPackagesLocked(userHandle);
+ updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
if (!policy.mStatusBarEnabledState) {
setStatusBarEnabledStateInternal(policy.mStatusBarEnabledState, userHandle);
}
}
- private void updateLockTaskPackagesLocked(DevicePolicyData policy, int userId) {
+ private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ am.updateLockTaskPackages(userId, packages.toArray(new String[packages.size()]));
+ } catch (RemoteException e) {
+ // Not gonna happen.
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void updateDeviceOwnerLocked() {
IActivityManager am = ActivityManagerNative.getDefault();
long ident = Binder.clearCallingIdentity();
try {
- am.updateLockTaskPackages(userId, policy.mLockTaskPackages.toArray(new String[0]));
+ am.updateDeviceOwner(getDeviceOwner());
} catch (RemoteException e) {
// Not gonna happen.
} finally {
@@ -3988,14 +4008,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (mDeviceOwner == null) {
// Device owner is not set and does not exist, set it.
mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
} else {
// Device owner is not set but a profile owner exists, update Device owner state.
mDeviceOwner.setDeviceOwner(packageName, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
}
+ mDeviceOwner.writeOwnerFile();
+ updateDeviceOwnerLocked();
+ return true;
}
}
@@ -4077,6 +4096,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (mDeviceOwner != null) {
mDeviceOwner.clearDeviceOwner();
mDeviceOwner.writeOwnerFile();
+ updateDeviceOwnerLocked();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4105,16 +4125,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (mDeviceOwner == null) {
// Device owner state does not exist, create it.
- mDeviceOwner = DeviceOwner.createWithDeviceInitializer(
- initializer, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
+ mDeviceOwner = DeviceOwner.createWithDeviceInitializer(initializer, ownerName);
} else {
// Device owner already exists, update it.
mDeviceOwner.setDeviceInitializer(initializer, ownerName);
- mDeviceOwner.writeOwnerFile();
- return true;
}
+
+ addDeviceInitializerToLockTaskPackagesLocked(UserHandle.USER_OWNER);
+ mDeviceOwner.writeOwnerFile();
+ return true;
}
}
@@ -5095,20 +5114,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final IPackageManager ipm = AppGlobals.getPackageManager();
IActivityManager activityManager = ActivityManagerNative.getDefault();
+ final int userHandle = user.getIdentifier();
try {
// Install the profile owner if not present.
- if (!ipm.isPackageAvailable(profileOwnerPkg, user.getIdentifier())) {
- ipm.installExistingPackageAsUser(profileOwnerPkg, user.getIdentifier());
+ if (!ipm.isPackageAvailable(profileOwnerPkg, userHandle)) {
+ ipm.installExistingPackageAsUser(profileOwnerPkg, userHandle);
}
// Start user in background.
- activityManager.startUserInBackground(user.getIdentifier());
+ activityManager.startUserInBackground(userHandle);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls for configureUser", e);
}
- setActiveAdmin(profileOwnerComponent, true, user.getIdentifier(), adminExtras);
- setProfileOwner(profileOwnerComponent, ownerName, user.getIdentifier());
+ setActiveAdmin(profileOwnerComponent, true, userHandle, adminExtras);
+ setProfileOwner(profileOwnerComponent, ownerName, userHandle);
return user;
} finally {
restoreCallingIdentity(id);
@@ -5653,29 +5673,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* This function can only be called by the device owner.
* @param packages The list of packages allowed to enter lock task mode.
*/
- public void setLockTaskPackages(ComponentName who, String[] packages) throws SecurityException {
+ public void setLockTaskPackages(ComponentName who, String[] packages)
+ throws SecurityException {
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
int userHandle = Binder.getCallingUserHandle().getIdentifier();
- DevicePolicyData policy = getUserData(userHandle);
- policy.mLockTaskPackages.clear();
- if (packages != null) {
- for (int j = 0; j < packages.length; j++) {
- String pkg = packages[j];
- if (pkg != null) {
- policy.mLockTaskPackages.add(pkg);
- }
- }
- }
-
- // Store the settings persistently.
- saveSettingsLocked(userHandle);
- updateLockTaskPackagesLocked(policy, userHandle);
+ setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
}
}
+ private void setLockTaskPackagesLocked(int userHandle, List<String> packages) {
+ DevicePolicyData policy = getUserData(userHandle);
+ policy.mLockTaskPackages = packages;
+
+ // Store the settings persistently.
+ saveSettingsLocked(userHandle);
+ updateLockTaskPackagesLocked(packages, userHandle);
+ }
+
/**
* This function returns the list of components allowed to start the task lock mode.
*/
@@ -5683,13 +5700,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
int userHandle = Binder.getCallingUserHandle().getIdentifier();
- DevicePolicyData policy = getUserData(userHandle);
- return policy.mLockTaskPackages.toArray(new String[policy.mLockTaskPackages.size()]);
+ final List<String> packages = getLockTaskPackagesLocked(userHandle);
+ return packages.toArray(new String[packages.size()]);
}
}
+ private List<String> getLockTaskPackagesLocked(int userHandle) {
+ final DevicePolicyData policy = getUserData(userHandle);
+ return policy.mLockTaskPackages;
+ }
+
/**
* This function lets the caller know whether the given package is allowed to start the
* lock task mode.
@@ -5953,6 +5974,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (!policy.mUserSetupComplete) {
policy.mUserSetupComplete = true;
synchronized (this) {
+ // The DeviceInitializer was whitelisted but now should be removed.
+ removeDeviceInitializerFromLockTaskPackages(userHandle);
saveSettingsLocked(userHandle);
}
}
@@ -5960,6 +5983,35 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ private void addDeviceInitializerToLockTaskPackagesLocked(int userHandle) {
+ if (hasUserSetupCompleted(userHandle)) {
+ return;
+ }
+
+ final String deviceInitializerPackage = getDeviceInitializer();
+ if (deviceInitializerPackage == null) {
+ return;
+ }
+
+ final List<String> packages = getLockTaskPackagesLocked(userHandle);
+ if (!packages.contains(deviceInitializerPackage)) {
+ packages.add(deviceInitializerPackage);
+ setLockTaskPackagesLocked(userHandle, packages);
+ }
+ }
+
+ private void removeDeviceInitializerFromLockTaskPackages(int userHandle) {
+ final String deviceInitializerPackage = getDeviceInitializer();
+ if (deviceInitializerPackage == null) {
+ return;
+ }
+
+ List<String> packages = getLockTaskPackagesLocked(userHandle);
+ if (packages.remove(deviceInitializerPackage)) {
+ setLockTaskPackagesLocked(userHandle, packages);
+ }
+ }
+
private class SetupContentObserver extends ContentObserver {
private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index bbd5e30ebb9b..2a3038475b23 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -66,9 +66,18 @@ public final class Call {
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 @@ public final class Call {
mVideoCall = parcelableCall.getVideoCall();
}
- int state = stateFromParcelableCallState(parcelableCall.getState());
+ int state = parcelableCall.getState();
boolean stateChanged = mState != state;
if (stateChanged) {
mState = state;
@@ -1064,32 +1073,4 @@ public final class Call {
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 558422622a06..000000000000
--- 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
new file mode 100644
index 000000000000..eef72fb198c4
--- /dev/null
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -0,0 +1,174 @@
+/*
+ * 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.telecom;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for managing the default dialer application that will receive incoming calls, and be
+ * allowed to make emergency outgoing calls.
+ *
+ * @hide
+ */
+public class DefaultDialerManager {
+ private static final String TAG = "DefaultDialerManager";
+
+ /**
+ * Sets the specified package name as the default dialer application. The caller of this method
+ * needs to have permission to write to secure settings.
+ *
+ * @hide
+ * */
+ public static void setDefaultPhoneApplication(Context context, String packageName) {
+ // Get old package name
+ String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION);
+
+ if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
+ // No change
+ return;
+ }
+
+ // Only make the change if the new package belongs to a valid phone application
+ List<ComponentName> componentNames = getInstalledDialerApplications(context);
+ final ComponentName foundComponent = getComponentName(componentNames, packageName);
+
+ if (foundComponent != null) {
+ // Update the secure setting.
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION, foundComponent.getPackageName());
+ }
+ }
+
+ /**
+ * Returns the installed dialer application that will be used to receive incoming calls, and is
+ * allowed to make emergency calls.
+ *
+ * The application will be returned in order of preference:
+ * 1) User selected phone application (if still installed)
+ * 2) Pre-installed system dialer (if not disabled)
+ * 3) Null
+ *
+ * @hide
+ * */
+ public static ComponentName getDefaultDialerApplication(Context context) {
+ String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.DIALER_DEFAULT_APPLICATION);
+
+ final List<ComponentName> componentNames = getInstalledDialerApplications(context);
+ if (!TextUtils.isEmpty(defaultPackageName)) {
+ final ComponentName defaultDialer =
+ getComponentName(componentNames, defaultPackageName);
+ if (defaultDialer != null) {
+ return defaultDialer;
+ }
+ }
+
+ // No user-set dialer found, fallback to system dialer
+ ComponentName systemDialer = getTelecomManager(context).getDefaultPhoneApp();
+
+ if (systemDialer == null) {
+ // No system dialer configured at build time
+ return null;
+ }
+
+ // Verify that the system dialer has not been disabled.
+ return getComponentName(componentNames, systemDialer.getPackageName());
+ }
+
+ /**
+ * Returns a list of installed and available dialer applications.
+ *
+ * In order to appear in the list, a dialer application must implement an intent-filter with
+ * the DIAL intent for the following schemes:
+ *
+ * 1) Empty scheme
+ * 2) tel Uri scheme
+ *
+ * @hide
+ **/
+ public static List<ComponentName> getInstalledDialerApplications(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+
+ // Get the list of apps registered for the DIAL intent with empty scheme
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
+
+ List<ComponentName> componentNames = new ArrayList<ComponentName> ();
+
+ for (ResolveInfo resolveInfo : resolveInfoList) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final ComponentName componentName =
+ new ComponentName(activityInfo.packageName, activityInfo.name);
+ componentNames.add(componentName);
+ }
+
+ // TODO: Filter for apps that don't handle DIAL intent with tel scheme
+ return componentNames;
+ }
+
+ /**
+ * Returns the {@link ComponentName} for the installed dialer application for a given package
+ * name.
+ *
+ * @param context A valid context.
+ * @param packageName to retrieve the {@link ComponentName} for.
+ *
+ * @return The {@link ComponentName} for the installed dialer application corresponding to the
+ * package name, or null if none is found.
+ *
+ * @hide
+ */
+ public static ComponentName getDialerApplicationForPackageName(Context context,
+ String packageName) {
+ return getComponentName(getInstalledDialerApplications(context), packageName);
+ }
+
+ /**
+ * Returns the component from a list of application components that corresponds to the package
+ * name.
+ *
+ * @param componentNames A list of component names
+ * @param packageName The package name to look for
+ * @return The {@link ComponentName} that matches the provided packageName, or null if not
+ * found.
+ */
+ private static ComponentName getComponentName(List<ComponentName> componentNames,
+ String packageName) {
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(packageName, componentName.getPackageName())) {
+ return componentName;
+ }
+ }
+ return null;
+ }
+
+ private static TelecomManager getTelecomManager(Context context) {
+ return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a72172cc91da..fd9532710224 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -17,6 +17,7 @@ package android.telecom;
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;
@@ -1057,6 +1058,39 @@ public class TelecomManager {
}
}
+ /**
+ * 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 727fd4bba30e..45b24827924d 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -210,4 +210,9 @@ interface ITelecomService {
* @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 d103fbf8d9ab..7d1a2fa9527e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -71,6 +71,11 @@ public class CarrierConfigManager {
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 @@ public class CarrierConfigManager {
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);
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index d10a7ea5ec41..9fea418b82c8 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -48,6 +48,13 @@ public class RadioAccessFamily implements Parcelable {
public static final int RAF_GSM = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
public static final int RAF_TD_SCDMA = (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA);
+ // Grouping of RAFs
+ private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
+ private static final int HS = RAF_HSUPA | RAF_HSDPA | RAF_HSPA | RAF_HSPAP;
+ private static final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
+ private static final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B | RAF_EHRPD;
+ private static final int WCDMA = HS | RAF_UMTS;
+
/* Phone ID of phone */
private int mPhoneId;
@@ -136,12 +143,6 @@ public class RadioAccessFamily implements Parcelable {
};
public static int getRafFromNetworkType(int type) {
- final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
- final int HS = RAF_HSUPA | RAF_HSDPA | RAF_HSPA | RAF_HSPAP;
- final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
- final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B;
- final int WCDMA = HS | RAF_UMTS;
-
int raf;
switch (type) {
@@ -158,7 +159,7 @@ public class RadioAccessFamily implements Parcelable {
raf = GSM | WCDMA;
break;
case RILConstants.NETWORK_MODE_CDMA:
- raf = CDMA;
+ raf = CDMA | EVDO;
break;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
raf = RAF_LTE | CDMA | EVDO;
@@ -188,7 +189,71 @@ public class RadioAccessFamily implements Parcelable {
raf = RAF_UNKNOWN;
break;
}
+
return raf;
}
+
+ /**
+ * if the raf includes ANY bit set for a group
+ * adjust it to contain ALL the bits for that group
+ */
+ private static int getAdjustedRaf(int raf) {
+ raf = ((GSM & raf) > 0) ? (GSM | raf) : raf;
+ raf = ((WCDMA & raf) > 0) ? (WCDMA | raf) : raf;
+ raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
+ raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
+
+ return raf;
+ }
+
+ public static int getNetworkTypeFromRaf(int raf) {
+ int type;
+
+ raf = getAdjustedRaf(raf);
+
+ switch (raf) {
+ case (GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_WCDMA_PREF;
+ break;
+ case GSM:
+ type = RILConstants.NETWORK_MODE_GSM_ONLY;
+ break;
+ case WCDMA:
+ type = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+ break;
+ case (CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_CDMA;
+ break;
+ case (RAF_LTE | CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+ break;
+ case (RAF_LTE | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+ break;
+ case (RAF_LTE | CDMA | EVDO | GSM | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+ break;
+ case RAF_LTE:
+ type = RILConstants.NETWORK_MODE_LTE_ONLY;
+ break;
+ case (RAF_LTE | WCDMA):
+ type = RILConstants.NETWORK_MODE_LTE_WCDMA;
+ break;
+ case CDMA:
+ type = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+ break;
+ case EVDO:
+ type = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+ break;
+ case (GSM | WCDMA | CDMA | EVDO):
+ type = RILConstants.NETWORK_MODE_GLOBAL;
+ break;
+ default:
+ type = RILConstants.PREFERRED_NETWORK_MODE ;
+ break;
+ }
+
+ return type;
+ }
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 276b71347fd2..9efea0d2cc5f 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.FeatureInfo;
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.ResolveInfo;
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,33 +488,62 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
+ @Override
+ public String getInstallerPackageName(String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
/** {@hide} */
@Override
- public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ public int getMoveStatus(int moveId) {
throw new UnsupportedOperationException();
}
/** {@hide} */
@Override
- public void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer) {
+ public void registerMoveCallback(MoveCallback callback, Handler handler) {
throw new UnsupportedOperationException();
}
/** {@hide} */
@Override
- public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
+ public void unregisterMoveCallback(MoveCallback callback) {
throw new UnsupportedOperationException();
}
/** {@hide} */
@Override
- public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+ public int movePackage(String packageName, VolumeInfo vol) {
throw new UnsupportedOperationException();
}
+ /** {@hide} */
@Override
- public String getInstallerPackageName(String packageName) {
+ 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();
}
diff --git a/tests/VoiceInteraction/res/layout/main.xml b/tests/VoiceInteraction/res/layout/main.xml
index 092d37dc4a20..34a7563f4eb2 100644
--- a/tests/VoiceInteraction/res/layout/main.xml
+++ b/tests/VoiceInteraction/res/layout/main.xml
@@ -34,6 +34,12 @@
android:text="@string/asyncStructure"
/>
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:assistBlocked="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="This won't be included in assist."
+ />
+
</LinearLayout>
diff --git a/tests/VoiceInteraction/res/xml/interaction_service.xml b/tests/VoiceInteraction/res/xml/interaction_service.xml
index ce5669cab1ea..789493a7f22c 100644
--- a/tests/VoiceInteraction/res/xml/interaction_service.xml
+++ b/tests/VoiceInteraction/res/xml/interaction_service.xml
@@ -20,4 +20,5 @@
<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
android:sessionService="com.android.test.voiceinteraction.MainInteractionSessionService"
android:recognitionService="com.android.test.voiceinteraction.MainRecognitionService"
- android:settingsActivity="com.android.test.voiceinteraction.SettingsActivity" />
+ android:settingsActivity="com.android.test.voiceinteraction.SettingsActivity"
+ android:supportsAssistGesture="true" />